@sirenodeck/sirenodeck 0.1.1-alpha.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 (485) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +69 -0
  3. package/bin/_wrapper-shared.js +42 -0
  4. package/bin/dev.js +95 -0
  5. package/bin/sirenodeck.js +42 -0
  6. package/config/default-config.yml +117 -0
  7. package/config/starter-config.yml +54 -0
  8. package/dist/main.mjs +64702 -0
  9. package/package.json +140 -0
  10. package/src/action/executor.ts +156 -0
  11. package/src/action/index.ts +7 -0
  12. package/src/addon/api-types.ts +15 -0
  13. package/src/addon/api.ts +461 -0
  14. package/src/addon/check-runner.ts +84 -0
  15. package/src/addon/index.ts +5 -0
  16. package/src/addon/loader.ts +441 -0
  17. package/src/addon/registry.ts +230 -0
  18. package/src/addon/spec.ts +69 -0
  19. package/src/api/addon.ts +33 -0
  20. package/src/api/asset-url.ts +24 -0
  21. package/src/api/index.ts +50 -0
  22. package/src/api/protocol-internal.ts +656 -0
  23. package/src/api/protocol.ts +2 -0
  24. package/src/api/react/index.ts +11 -0
  25. package/src/api/react/registry.ts +91 -0
  26. package/src/api/react/use-addon-channel.ts +26 -0
  27. package/src/api/react/use-button-state.tsx +23 -0
  28. package/src/api/react/use-deck.ts +24 -0
  29. package/src/assets/logo.png +0 -0
  30. package/src/assets/logo72x72.png +0 -0
  31. package/src/assets/logoFull.png +0 -0
  32. package/src/builtin-addons/brightness/README.md +51 -0
  33. package/src/builtin-addons/brightness/buttons/brightness/backend.ts +13 -0
  34. package/src/builtin-addons/brightness/buttons/brightness/config.ts +10 -0
  35. package/src/builtin-addons/brightness/buttons/brightness/frontend.tsx +50 -0
  36. package/src/builtin-addons/brightness/domain/macos.ts +46 -0
  37. package/src/builtin-addons/brightness/index.d.ts +5 -0
  38. package/src/builtin-addons/brightness/index.ts +20 -0
  39. package/src/builtin-addons/brightness/poller.ts +27 -0
  40. package/src/builtin-addons/brightness/providers/darwin.ts +107 -0
  41. package/src/builtin-addons/brightness/providers/index.ts +81 -0
  42. package/src/builtin-addons/brightness/providers/linux.ts +108 -0
  43. package/src/builtin-addons/brightness/providers/windows.ts +77 -0
  44. package/src/builtin-addons/brightness/sirenodeck.json +6 -0
  45. package/src/builtin-addons/coding-agents/assets/claude-code.svg +1 -0
  46. package/src/builtin-addons/coding-agents/assets/opencode-dark-square.svg +1 -0
  47. package/src/builtin-addons/coding-agents/buttons/agent/backend.ts +82 -0
  48. package/src/builtin-addons/coding-agents/buttons/agent/config.ts +13 -0
  49. package/src/builtin-addons/coding-agents/buttons/agent/frontend.tsx +130 -0
  50. package/src/builtin-addons/coding-agents/buttons/agent/pages.tsx +150 -0
  51. package/src/builtin-addons/coding-agents/buttons/summary/backend.ts +48 -0
  52. package/src/builtin-addons/coding-agents/buttons/summary/config.ts +11 -0
  53. package/src/builtin-addons/coding-agents/buttons/summary/frontend.tsx +254 -0
  54. package/src/builtin-addons/coding-agents/decks/agents.ts +69 -0
  55. package/src/builtin-addons/coding-agents/global/backend.ts +402 -0
  56. package/src/builtin-addons/coding-agents/global-entry.ts +7 -0
  57. package/src/builtin-addons/coding-agents/hook/script.ts +123 -0
  58. package/src/builtin-addons/coding-agents/index.ts +15 -0
  59. package/src/builtin-addons/coding-agents/manifest.ts +115 -0
  60. package/src/builtin-addons/coding-agents/providers/claude-code.ts +470 -0
  61. package/src/builtin-addons/coding-agents/providers/claude-instances.ts +184 -0
  62. package/src/builtin-addons/coding-agents/providers/opencode-instances.ts +108 -0
  63. package/src/builtin-addons/coding-agents/providers/opencode.ts +636 -0
  64. package/src/builtin-addons/coding-agents/providers/registry.ts +120 -0
  65. package/src/builtin-addons/coding-agents/providers/spawn.ts +112 -0
  66. package/src/builtin-addons/coding-agents/shared/claude-hook-events.ts +55 -0
  67. package/src/builtin-addons/coding-agents/shared/claude-status.ts +236 -0
  68. package/src/builtin-addons/coding-agents/shared/live-count.ts +39 -0
  69. package/src/builtin-addons/coding-agents/shared/notifier.ts +58 -0
  70. package/src/builtin-addons/coding-agents/shared/opencode-status.ts +82 -0
  71. package/src/builtin-addons/coding-agents/shared/snapshot.ts +66 -0
  72. package/src/builtin-addons/coding-agents/shared/state.ts +72 -0
  73. package/src/builtin-addons/coding-agents/sirenodeck.json +6 -0
  74. package/src/builtin-addons/coding-agents/types/types.ts +33 -0
  75. package/src/builtin-addons/core/README.md +89 -0
  76. package/src/builtin-addons/core/buttons/action/backend.ts +7 -0
  77. package/src/builtin-addons/core/buttons/action/config.ts +13 -0
  78. package/src/builtin-addons/core/buttons/action/frontend.tsx +16 -0
  79. package/src/builtin-addons/core/buttons/change-deck/backend.ts +18 -0
  80. package/src/builtin-addons/core/buttons/change-deck/config.ts +15 -0
  81. package/src/builtin-addons/core/buttons/change-deck/frontend.tsx +18 -0
  82. package/src/builtin-addons/core/buttons/media-sample/backend.ts +8 -0
  83. package/src/builtin-addons/core/buttons/media-sample/config.ts +7 -0
  84. package/src/builtin-addons/core/buttons/media-sample/frontend.tsx +2 -0
  85. package/src/builtin-addons/core/buttons/page-nav/backend.ts +28 -0
  86. package/src/builtin-addons/core/buttons/page-nav/config.ts +9 -0
  87. package/src/builtin-addons/core/buttons/page-nav/frontend.tsx +68 -0
  88. package/src/builtin-addons/core/buttons/toggle/backend.ts +119 -0
  89. package/src/builtin-addons/core/buttons/toggle/config.ts +57 -0
  90. package/src/builtin-addons/core/buttons/toggle/frontend.tsx +57 -0
  91. package/src/builtin-addons/core/buttons/toggle/global-service.ts +174 -0
  92. package/src/builtin-addons/core/index.ts +120 -0
  93. package/src/builtin-addons/core/sirenodeck.json +6 -0
  94. package/src/builtin-addons/date-time/README.md +73 -0
  95. package/src/builtin-addons/date-time/buttons/analog-clock/backend.ts +10 -0
  96. package/src/builtin-addons/date-time/buttons/analog-clock/config.ts +4 -0
  97. package/src/builtin-addons/date-time/buttons/analog-clock/frontend.tsx +88 -0
  98. package/src/builtin-addons/date-time/buttons/custom/backend.ts +10 -0
  99. package/src/builtin-addons/date-time/buttons/custom/config.ts +9 -0
  100. package/src/builtin-addons/date-time/buttons/custom/frontend.tsx +26 -0
  101. package/src/builtin-addons/date-time/buttons/date/backend.ts +10 -0
  102. package/src/builtin-addons/date-time/buttons/date/config.ts +10 -0
  103. package/src/builtin-addons/date-time/buttons/date/frontend.tsx +42 -0
  104. package/src/builtin-addons/date-time/buttons/time/backend.ts +10 -0
  105. package/src/builtin-addons/date-time/buttons/time/config.ts +9 -0
  106. package/src/builtin-addons/date-time/buttons/time/frontend.tsx +29 -0
  107. package/src/builtin-addons/date-time/index.d.ts +5 -0
  108. package/src/builtin-addons/date-time/index.ts +30 -0
  109. package/src/builtin-addons/date-time/poller.ts +11 -0
  110. package/src/builtin-addons/date-time/shared/format-date.ts +36 -0
  111. package/src/builtin-addons/date-time/shared/format.ts +105 -0
  112. package/src/builtin-addons/date-time/shared/use-now.ts +10 -0
  113. package/src/builtin-addons/date-time/sirenodeck.json +6 -0
  114. package/src/builtin-addons/emoji-selector/README.md +45 -0
  115. package/src/builtin-addons/emoji-selector/assets/launcher.svg +10 -0
  116. package/src/builtin-addons/emoji-selector/assets/smileys.svg +8 -0
  117. package/src/builtin-addons/emoji-selector/buttons/category/backend.ts +14 -0
  118. package/src/builtin-addons/emoji-selector/buttons/category/config.ts +8 -0
  119. package/src/builtin-addons/emoji-selector/buttons/category/frontend.tsx +19 -0
  120. package/src/builtin-addons/emoji-selector/buttons/emoji/backend.ts +7 -0
  121. package/src/builtin-addons/emoji-selector/buttons/emoji/config.ts +9 -0
  122. package/src/builtin-addons/emoji-selector/buttons/emoji/frontend.tsx +11 -0
  123. package/src/builtin-addons/emoji-selector/buttons/launcher/backend.ts +13 -0
  124. package/src/builtin-addons/emoji-selector/buttons/launcher/config.ts +6 -0
  125. package/src/builtin-addons/emoji-selector/buttons/launcher/frontend.tsx +31 -0
  126. package/src/builtin-addons/emoji-selector/data/categories.ts +1084 -0
  127. package/src/builtin-addons/emoji-selector/decks/index.ts +137 -0
  128. package/src/builtin-addons/emoji-selector/index.d.ts +5 -0
  129. package/src/builtin-addons/emoji-selector/index.ts +29 -0
  130. package/src/builtin-addons/emoji-selector/sirenodeck.json +6 -0
  131. package/src/builtin-addons/emoji-selector/support.ts +49 -0
  132. package/src/builtin-addons/index.ts +13 -0
  133. package/src/builtin-addons/internal-settings/README.md +26 -0
  134. package/src/builtin-addons/internal-settings/backend.ts +58 -0
  135. package/src/builtin-addons/internal-settings/buttons/app-info/backend.ts +7 -0
  136. package/src/builtin-addons/internal-settings/buttons/app-info/config.ts +13 -0
  137. package/src/builtin-addons/internal-settings/buttons/app-info/frontend.tsx +18 -0
  138. package/src/builtin-addons/internal-settings/buttons/brightness/common/frontend.tsx +50 -0
  139. package/src/builtin-addons/internal-settings/buttons/brightness-down/backend.ts +16 -0
  140. package/src/builtin-addons/internal-settings/buttons/brightness-down/config.ts +6 -0
  141. package/src/builtin-addons/internal-settings/buttons/brightness-down/frontend.tsx +8 -0
  142. package/src/builtin-addons/internal-settings/buttons/brightness-up/backend.ts +16 -0
  143. package/src/builtin-addons/internal-settings/buttons/brightness-up/config.ts +6 -0
  144. package/src/builtin-addons/internal-settings/buttons/brightness-up/frontend.tsx +8 -0
  145. package/src/builtin-addons/internal-settings/decks/settings.ts +25 -0
  146. package/src/builtin-addons/internal-settings/index.d.ts +4 -0
  147. package/src/builtin-addons/internal-settings/index.ts +53 -0
  148. package/src/builtin-addons/internal-settings/logo72x72.png +0 -0
  149. package/src/builtin-addons/internal-settings/sirenodeck.json +6 -0
  150. package/src/builtin-addons/media/README.md +45 -0
  151. package/src/builtin-addons/media/backend.ts +143 -0
  152. package/src/builtin-addons/media/buttons/media-player/backend.ts +19 -0
  153. package/src/builtin-addons/media/buttons/media-player/components/MediaStatus.tsx +25 -0
  154. package/src/builtin-addons/media/buttons/media-player/components/MediaSurface.tsx +52 -0
  155. package/src/builtin-addons/media/buttons/media-player/components/ProgressBar.tsx +21 -0
  156. package/src/builtin-addons/media/buttons/media-player/components/status-meta.ts +36 -0
  157. package/src/builtin-addons/media/buttons/media-player/config.ts +4 -0
  158. package/src/builtin-addons/media/buttons/media-player/frontend.tsx +29 -0
  159. package/src/builtin-addons/media/buttons/volume/common/frontend.tsx +52 -0
  160. package/src/builtin-addons/media/buttons/volume/mute/backend.ts +13 -0
  161. package/src/builtin-addons/media/buttons/volume/mute/config.ts +4 -0
  162. package/src/builtin-addons/media/buttons/volume/mute/frontend.tsx +26 -0
  163. package/src/builtin-addons/media/buttons/volume/volume-down/backend.ts +14 -0
  164. package/src/builtin-addons/media/buttons/volume/volume-down/config.ts +6 -0
  165. package/src/builtin-addons/media/buttons/volume/volume-down/frontend.tsx +10 -0
  166. package/src/builtin-addons/media/buttons/volume/volume-up/backend.ts +14 -0
  167. package/src/builtin-addons/media/buttons/volume/volume-up/config.ts +6 -0
  168. package/src/builtin-addons/media/buttons/volume/volume-up/frontend.tsx +8 -0
  169. package/src/builtin-addons/media/checks.ts +104 -0
  170. package/src/builtin-addons/media/executor.ts +36 -0
  171. package/src/builtin-addons/media/index.d.ts +5 -0
  172. package/src/builtin-addons/media/index.ts +53 -0
  173. package/src/builtin-addons/media/progress.ts +16 -0
  174. package/src/builtin-addons/media/providers/darwin.ts +391 -0
  175. package/src/builtin-addons/media/providers/index.ts +27 -0
  176. package/src/builtin-addons/media/providers/linux.ts +196 -0
  177. package/src/builtin-addons/media/providers/types.ts +48 -0
  178. package/src/builtin-addons/media/providers/windows.ts +121 -0
  179. package/src/builtin-addons/media/sirenodeck.json +6 -0
  180. package/src/builtin-addons/media/state.ts +19 -0
  181. package/src/builtin-addons/register-builtins.ts +65 -0
  182. package/src/builtin-addons/session/README.md +14 -0
  183. package/src/builtin-addons/session/buttons/session-info/backend.ts +8 -0
  184. package/src/builtin-addons/session/buttons/session-info/config.ts +4 -0
  185. package/src/builtin-addons/session/buttons/session-info/frontend.tsx +2 -0
  186. package/src/builtin-addons/session/buttons/time/backend.ts +7 -0
  187. package/src/builtin-addons/session/buttons/time/config.ts +6 -0
  188. package/src/builtin-addons/session/buttons/time/frontend.tsx +2 -0
  189. package/src/builtin-addons/session/index.d.ts +5 -0
  190. package/src/builtin-addons/session/index.ts +18 -0
  191. package/src/builtin-addons/session/sirenodeck.json +6 -0
  192. package/src/builtin-addons/system-status/README.md +169 -0
  193. package/src/builtin-addons/system-status/buttons/_shared.ts +131 -0
  194. package/src/builtin-addons/system-status/buttons/system-status/frontend.tsx +224 -0
  195. package/src/builtin-addons/system-status/buttons/system-status/schemas.ts +67 -0
  196. package/src/builtin-addons/system-status/domain/catalog.ts +53 -0
  197. package/src/builtin-addons/system-status/domain/chart-sampler.ts +58 -0
  198. package/src/builtin-addons/system-status/domain/display-metrics.ts +128 -0
  199. package/src/builtin-addons/system-status/domain/index.ts +10 -0
  200. package/src/builtin-addons/system-status/domain/live-metrics.ts +1021 -0
  201. package/src/builtin-addons/system-status/domain/metric-ids.ts +0 -0
  202. package/src/builtin-addons/system-status/index.ts +16 -0
  203. package/src/builtin-addons/system-status/manifest.ts +105 -0
  204. package/src/builtin-addons/system-status/shared/metrics-catalog.ts +316 -0
  205. package/src/builtin-addons/system-status/sirenodeck.json +6 -0
  206. package/src/builtin-addons/test-buildin/decks/action-buttons-test.ts +56 -0
  207. package/src/builtin-addons/test-buildin/index.d.ts +5 -0
  208. package/src/builtin-addons/test-buildin/index.ts +16 -0
  209. package/src/builtin-addons/value-display/README.md +59 -0
  210. package/src/builtin-addons/value-display/backend.ts +86 -0
  211. package/src/builtin-addons/value-display/buttons/value-display/backend.ts +17 -0
  212. package/src/builtin-addons/value-display/buttons/value-display/config.ts +36 -0
  213. package/src/builtin-addons/value-display/buttons/value-display/frontend.tsx +54 -0
  214. package/src/builtin-addons/value-display/index.d.ts +5 -0
  215. package/src/builtin-addons/value-display/index.ts +23 -0
  216. package/src/builtin-addons/value-display/sirenodeck.json +6 -0
  217. package/src/builtin-addons/weather/README.md +46 -0
  218. package/src/builtin-addons/weather/backend.ts +121 -0
  219. package/src/builtin-addons/weather/buttons/weather/backend.ts +27 -0
  220. package/src/builtin-addons/weather/buttons/weather/config.ts +29 -0
  221. package/src/builtin-addons/weather/buttons/weather/frontend.tsx +63 -0
  222. package/src/builtin-addons/weather/buttons/weather/icons.tsx +88 -0
  223. package/src/builtin-addons/weather/buttons/weather/pages.tsx +109 -0
  224. package/src/builtin-addons/weather/domain/codes.ts +35 -0
  225. package/src/builtin-addons/weather/domain/fetch.ts +47 -0
  226. package/src/builtin-addons/weather/index.d.ts +5 -0
  227. package/src/builtin-addons/weather/index.ts +23 -0
  228. package/src/builtin-addons/weather/poller.ts +70 -0
  229. package/src/builtin-addons/weather/provider/city-key.ts +9 -0
  230. package/src/builtin-addons/weather/provider/codes.ts +35 -0
  231. package/src/builtin-addons/weather/provider/fetch.ts +97 -0
  232. package/src/builtin-addons/weather/provider/geocode.ts +67 -0
  233. package/src/builtin-addons/weather/provider/types.ts +42 -0
  234. package/src/builtin-addons/weather/sirenodeck.json +6 -0
  235. package/src/cli/coding-agents-onboarding.ts +434 -0
  236. package/src/cli/commands/addon-decks.ts +441 -0
  237. package/src/cli/commands/addon-registry.ts +639 -0
  238. package/src/cli/commands/emulator-mode.ts +289 -0
  239. package/src/cli/commands/install.ts +128 -0
  240. package/src/cli/commands/logs.ts +74 -0
  241. package/src/cli/commands/network-bind.ts +111 -0
  242. package/src/cli/commands/pipeline/helpers.ts +75 -0
  243. package/src/cli/commands/pipeline/preflight.ts +20 -0
  244. package/src/cli/commands/port-identity.ts +152 -0
  245. package/src/cli/commands/reload.ts +37 -0
  246. package/src/cli/commands/restart.ts +139 -0
  247. package/src/cli/commands/run.ts +2270 -0
  248. package/src/cli/commands/service/install.ts +121 -0
  249. package/src/cli/commands/service/render-template.ts +158 -0
  250. package/src/cli/commands/service-manager.ts +184 -0
  251. package/src/cli/commands/spawn-daemon.ts +251 -0
  252. package/src/cli/commands/start.ts +1061 -0
  253. package/src/cli/commands/status.ts +110 -0
  254. package/src/cli/commands/stop.ts +121 -0
  255. package/src/cli/commands/subprocess-supervisor.ts +203 -0
  256. package/src/cli/commands/system-requirements.ts +699 -0
  257. package/src/cli/cwd.ts +28 -0
  258. package/src/cli/first-run.ts +149 -0
  259. package/src/cli/http-server.ts +244 -0
  260. package/src/cli/index.ts +355 -0
  261. package/src/cli/install-root.ts +26 -0
  262. package/src/cli/main.ts +194 -0
  263. package/src/cli/package-manager.ts +175 -0
  264. package/src/cli/probe-deps.ts +45 -0
  265. package/src/cli/prompt.ts +22 -0
  266. package/src/cli/startup-display.ts +452 -0
  267. package/src/config/builtin-icons.ts +57 -0
  268. package/src/config/config-diff.ts +54 -0
  269. package/src/config/discovery.ts +64 -0
  270. package/src/config/icon-resolver.ts +90 -0
  271. package/src/config/include-resolver.ts +202 -0
  272. package/src/config/index.ts +9 -0
  273. package/src/config/loader.ts +187 -0
  274. package/src/config/mutation.ts +514 -0
  275. package/src/config/reference-expander.ts +84 -0
  276. package/src/config/schemas.ts +223 -0
  277. package/src/config/validation.ts +254 -0
  278. package/src/core/asset-id.ts +21 -0
  279. package/src/core/gesture-state.ts +344 -0
  280. package/src/core/icon-asset-registry.ts +127 -0
  281. package/src/core/icon-source.ts +86 -0
  282. package/src/core/index.ts +30 -0
  283. package/src/core/mime.ts +16 -0
  284. package/src/core/pagination.ts +90 -0
  285. package/src/core/pub-sub.ts +126 -0
  286. package/src/core/settings.ts +153 -0
  287. package/src/core/store.ts +88 -0
  288. package/src/core/watcher.ts +73 -0
  289. package/src/deck/addon-handler-bridge.ts +516 -0
  290. package/src/deck/deck-config.ts +271 -0
  291. package/src/deck/editor-surfaces.ts +87 -0
  292. package/src/deck/host-context.ts +18 -0
  293. package/src/deck/index.ts +102 -0
  294. package/src/deck/macro-parse.ts +182 -0
  295. package/src/deck/methods.ts +348 -0
  296. package/src/deck/paginate-deck.ts +98 -0
  297. package/src/deck/position-buttons.ts +57 -0
  298. package/src/deck/runtime/index.ts +5 -0
  299. package/src/deck/runtime/runtime.ts +1060 -0
  300. package/src/deck/runtime-subscriptions.ts +45 -0
  301. package/src/deck/system-back-injection.ts +62 -0
  302. package/src/deck/system-buttons/registry.tsx +97 -0
  303. package/src/deck/system-buttons/types.ts +14 -0
  304. package/src/device/black-frame.ts +73 -0
  305. package/src/device/index.ts +13 -0
  306. package/src/device/linux-udev.ts +36 -0
  307. package/src/device/models.ts +89 -0
  308. package/src/device/reconnecting.ts +193 -0
  309. package/src/device/registry.ts +38 -0
  310. package/src/device/stream-deck.ts +155 -0
  311. package/src/index.ts +74 -0
  312. package/src/outputClient/emulator.ts +357 -0
  313. package/src/outputClient/index.ts +26 -0
  314. package/src/outputClient/open-browser.ts +26 -0
  315. package/src/outputClient/real.ts +393 -0
  316. package/src/outputClient/types.ts +83 -0
  317. package/src/render/browser-renderer.ts +240 -0
  318. package/src/render/buffer-hash.ts +20 -0
  319. package/src/render/editor-handler.ts +197 -0
  320. package/src/render/emulator-server.ts +128 -0
  321. package/src/render/icon-source-resolver.ts +69 -0
  322. package/src/render/index.ts +16 -0
  323. package/src/render/protocol.ts +61 -0
  324. package/src/render/push-raw-image.ts +100 -0
  325. package/src/render/screenshot-cadence.ts +87 -0
  326. package/src/render/state-publisher.ts +110 -0
  327. package/src/render/vite-server.ts +138 -0
  328. package/src/render/ws-bridge.ts +315 -0
  329. package/src/system/device-selection.ts +80 -0
  330. package/src/system/glob-match.ts +74 -0
  331. package/src/system/providers/active-app/darwin.ts +117 -0
  332. package/src/system/providers/active-app/linux.ts +245 -0
  333. package/src/system/providers/active-app/wayland-gnome.ts +303 -0
  334. package/src/system/providers/active-app/windows.ts +94 -0
  335. package/src/system/providers/active-app.ts +97 -0
  336. package/src/system/providers/clipboard/darwin.ts +65 -0
  337. package/src/system/providers/clipboard/linux.ts +163 -0
  338. package/src/system/providers/clipboard/windows.ts +94 -0
  339. package/src/system/providers/clipboard.ts +84 -0
  340. package/src/system/providers/error.ts +16 -0
  341. package/src/system/providers/key-macro/darwin.ts +221 -0
  342. package/src/system/providers/key-macro/linux.ts +651 -0
  343. package/src/system/providers/key-macro/parser.ts +215 -0
  344. package/src/system/providers/key-macro/windows.ts +476 -0
  345. package/src/system/providers/key-macro.ts +79 -0
  346. package/src/system/providers/notification/darwin.ts +36 -0
  347. package/src/system/providers/notification/linux.ts +115 -0
  348. package/src/system/providers/notification/windows.ts +72 -0
  349. package/src/system/providers/notification.ts +75 -0
  350. package/src/system/providers/session/darwin.ts +82 -0
  351. package/src/system/providers/session/linux.ts +175 -0
  352. package/src/system/providers/session/windows.ts +73 -0
  353. package/src/system/providers/session.ts +89 -0
  354. package/src/system/providers/shared.ts +65 -0
  355. package/src/system/requirements.ts +235 -0
  356. package/src/system/setup-wizard/config-seed.ts +30 -0
  357. package/src/system/setup-wizard/format.ts +275 -0
  358. package/src/system/setup-wizard/index.ts +45 -0
  359. package/src/system/setup-wizard/open-settings.ts +26 -0
  360. package/src/system/setup-wizard/plan.ts +230 -0
  361. package/src/system/setup-wizard/probe-cache.ts +34 -0
  362. package/src/system/setup-wizard/probe.ts +486 -0
  363. package/src/system/setup-wizard/runtime-features.ts +85 -0
  364. package/src/system/setup-wizard/sudo.ts +178 -0
  365. package/src/system/setup-wizard/types.ts +133 -0
  366. package/src/system/virtual-stream-deck.ts +67 -0
  367. package/src/themes/css.ts +210 -0
  368. package/src/themes/default/assets/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  369. package/src/themes/default/assets/IBMPlexSans-VariableFont_wdth,wght.ttf +0 -0
  370. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Bold.ttf +0 -0
  371. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-BoldItalic.ttf +0 -0
  372. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ExtraLight.ttf +0 -0
  373. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ExtraLightItalic.ttf +0 -0
  374. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Italic.ttf +0 -0
  375. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Light.ttf +0 -0
  376. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-LightItalic.ttf +0 -0
  377. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Medium.ttf +0 -0
  378. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-MediumItalic.ttf +0 -0
  379. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Regular.ttf +0 -0
  380. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-SemiBold.ttf +0 -0
  381. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-SemiBoldItalic.ttf +0 -0
  382. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Thin.ttf +0 -0
  383. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ThinItalic.ttf +0 -0
  384. package/src/themes/default/assets/IBM_Plex_Mono/OFL.txt +93 -0
  385. package/src/themes/default/assets/OFL.txt +93 -0
  386. package/src/themes/default/assets/README.txt +106 -0
  387. package/src/themes/default/assets/static/IBMPlexSans-Bold.ttf +0 -0
  388. package/src/themes/default/assets/static/IBMPlexSans-BoldItalic.ttf +0 -0
  389. package/src/themes/default/assets/static/IBMPlexSans-ExtraLight.ttf +0 -0
  390. package/src/themes/default/assets/static/IBMPlexSans-ExtraLightItalic.ttf +0 -0
  391. package/src/themes/default/assets/static/IBMPlexSans-Italic.ttf +0 -0
  392. package/src/themes/default/assets/static/IBMPlexSans-Light.ttf +0 -0
  393. package/src/themes/default/assets/static/IBMPlexSans-LightItalic.ttf +0 -0
  394. package/src/themes/default/assets/static/IBMPlexSans-Medium.ttf +0 -0
  395. package/src/themes/default/assets/static/IBMPlexSans-MediumItalic.ttf +0 -0
  396. package/src/themes/default/assets/static/IBMPlexSans-Regular.ttf +0 -0
  397. package/src/themes/default/assets/static/IBMPlexSans-SemiBold.ttf +0 -0
  398. package/src/themes/default/assets/static/IBMPlexSans-SemiBoldItalic.ttf +0 -0
  399. package/src/themes/default/assets/static/IBMPlexSans-Thin.ttf +0 -0
  400. package/src/themes/default/assets/static/IBMPlexSans-ThinItalic.ttf +0 -0
  401. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Bold.ttf +0 -0
  402. package/src/themes/default/assets/static/IBMPlexSans_Condensed-BoldItalic.ttf +0 -0
  403. package/src/themes/default/assets/static/IBMPlexSans_Condensed-ExtraLight.ttf +0 -0
  404. package/src/themes/default/assets/static/IBMPlexSans_Condensed-ExtraLightItalic.ttf +0 -0
  405. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Italic.ttf +0 -0
  406. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Light.ttf +0 -0
  407. package/src/themes/default/assets/static/IBMPlexSans_Condensed-LightItalic.ttf +0 -0
  408. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Medium.ttf +0 -0
  409. package/src/themes/default/assets/static/IBMPlexSans_Condensed-MediumItalic.ttf +0 -0
  410. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Regular.ttf +0 -0
  411. package/src/themes/default/assets/static/IBMPlexSans_Condensed-SemiBold.ttf +0 -0
  412. package/src/themes/default/assets/static/IBMPlexSans_Condensed-SemiBoldItalic.ttf +0 -0
  413. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Thin.ttf +0 -0
  414. package/src/themes/default/assets/static/IBMPlexSans_Condensed-ThinItalic.ttf +0 -0
  415. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Bold.ttf +0 -0
  416. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-BoldItalic.ttf +0 -0
  417. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ExtraLight.ttf +0 -0
  418. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ExtraLightItalic.ttf +0 -0
  419. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Italic.ttf +0 -0
  420. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Light.ttf +0 -0
  421. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-LightItalic.ttf +0 -0
  422. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Medium.ttf +0 -0
  423. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-MediumItalic.ttf +0 -0
  424. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Regular.ttf +0 -0
  425. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-SemiBold.ttf +0 -0
  426. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
  427. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Thin.ttf +0 -0
  428. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ThinItalic.ttf +0 -0
  429. package/src/themes/default/components.css +81 -0
  430. package/src/themes/default/sirenodeck.json +170 -0
  431. package/src/themes/index.ts +14 -0
  432. package/src/themes/light/assets/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  433. package/src/themes/light/assets/IBMPlexSans-VariableFont_wdth,wght.ttf +0 -0
  434. package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Bold.ttf +0 -0
  435. package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Medium.ttf +0 -0
  436. package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Regular.ttf +0 -0
  437. package/src/themes/light/assets/IBM_Plex_Mono/OFL.txt +93 -0
  438. package/src/themes/light/assets/OFL.txt +93 -0
  439. package/src/themes/light/assets/README.txt +106 -0
  440. package/src/themes/light/assets/static/IBMPlexSans-Light.ttf +0 -0
  441. package/src/themes/light/assets/static/IBMPlexSans-LightItalic.ttf +0 -0
  442. package/src/themes/light/assets/static/IBMPlexSans-Thin.ttf +0 -0
  443. package/src/themes/light/components.css +70 -0
  444. package/src/themes/light/sirenodeck.json +170 -0
  445. package/src/themes/loader.ts +343 -0
  446. package/src/themes/manifest.ts +164 -0
  447. package/src/themes/use-resolved-theme.tsx +32 -0
  448. package/src/ui/ButtonFrame.tsx +230 -0
  449. package/src/ui/contexts/AssetCacheContext.tsx +43 -0
  450. package/src/ui/index.ts +20 -0
  451. package/src/ui/primitives/Chip.tsx +53 -0
  452. package/src/ui/primitives/Icon.tsx +209 -0
  453. package/src/ui/primitives/Label.tsx +82 -0
  454. package/src/ui/primitives/ProgressBar.tsx +38 -0
  455. package/src/ui/primitives/TapIndicator.tsx +67 -0
  456. package/src/ui/primitives/Text.tsx +810 -0
  457. package/src/ui/primitives/index.ts +6 -0
  458. package/src/ui/surfaces/BarsSurface.tsx +211 -0
  459. package/src/ui/surfaces/IconLabelProgressSurface.tsx +133 -0
  460. package/src/ui/surfaces/IconLabelSurface.tsx +59 -0
  461. package/src/ui/surfaces/LabelValueListSurface.tsx +202 -0
  462. package/src/ui/surfaces/PaginatedSurface.tsx +164 -0
  463. package/src/ui/surfaces/SplitActionSurface.tsx +98 -0
  464. package/src/ui/surfaces/TemporaryErrorSurface.tsx +68 -0
  465. package/src/ui/surfaces/ValueChart.tsx +218 -0
  466. package/src/ui/surfaces/index.ts +32 -0
  467. package/src/ui/surfaces/paginated-schema.ts +22 -0
  468. package/src/ui/theme-presentation-builder.ts +184 -0
  469. package/src/ui/theme-presentation.tsx +146 -0
  470. package/src/ui/utils/cn.ts +12 -0
  471. package/src/util/cache-paths.ts +38 -0
  472. package/src/util/config-diff.ts +16 -0
  473. package/src/util/daemon-control.ts +85 -0
  474. package/src/util/daemon.ts +575 -0
  475. package/src/util/device-config.ts +56 -0
  476. package/src/util/errors.ts +13 -0
  477. package/src/util/log-reader.ts +114 -0
  478. package/src/util/log-tail.ts +103 -0
  479. package/src/util/logger.ts +417 -0
  480. package/src/util/process-title.ts +79 -0
  481. package/src/util/single-instance.ts +202 -0
  482. package/src/version.ts +10 -0
  483. package/src/vite/index.ts +6 -0
  484. package/src/vite/virtual-modules.ts +631 -0
  485. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,434 @@
1
+ import {
2
+ copyFileSync,
3
+ existsSync,
4
+ mkdirSync,
5
+ readFileSync,
6
+ renameSync,
7
+ unlinkSync,
8
+ writeFileSync,
9
+ } from "node:fs"
10
+ import { homedir } from "node:os"
11
+ import { join } from "node:path"
12
+
13
+ import { confirm, isCancel, note } from "@/cli/prompt"
14
+ import { loadConfig } from "@/config/loader"
15
+ import { CLAUDE_HOOK_REGISTRATIONS } from "@/builtin-addons/coding-agents/shared/claude-hook-events"
16
+ import {
17
+ CLAUDE_HOOK_FILENAME,
18
+ CLAUDE_HOOK_MARKER,
19
+ CLAUDE_HOOK_VERSION,
20
+ claudeHookSource,
21
+ } from "@/builtin-addons/coding-agents/hook/script"
22
+ import { applyEdits, modify, parse, type ParseError } from "jsonc-parser"
23
+
24
+ const PLUGIN_FILE = "sirenodeck-agent-state.js"
25
+ const PLUGIN_SPEC = `./plugins/${PLUGIN_FILE}`
26
+ const PLUGIN_MARKER = "SIRENODECK_INTEGRATION_ID=coding-agents-v2"
27
+
28
+ const opencodeConfigDir = (): string =>
29
+ process.env["OPENCODE_CONFIG_DIR"] ??
30
+ join(process.env["XDG_CONFIG_HOME"] ?? join(homedir(), ".config"), "opencode")
31
+
32
+ const pluginDir = (): string => join(opencodeConfigDir(), "plugins")
33
+
34
+ const opencodeConfigPath = (): string =>
35
+ join(opencodeConfigDir(), "opencode.json")
36
+
37
+ export const opencodePluginPath = (): string => join(pluginDir(), PLUGIN_FILE)
38
+
39
+ export const isCodingAgentsConfigured = (configPath: string): boolean => {
40
+ if (!existsSync(configPath)) return false
41
+ try {
42
+ return JSON.stringify(loadConfig({ configPath }).config).includes(
43
+ "coding-agents:",
44
+ )
45
+ } catch {
46
+ return false
47
+ }
48
+ }
49
+
50
+ export const isOpenCodePluginInstalled = (): boolean => {
51
+ try {
52
+ return readFileSync(opencodePluginPath(), "utf8").includes(PLUGIN_MARKER)
53
+ } catch {
54
+ return false
55
+ }
56
+ }
57
+
58
+ const readOpenCodeConfig = (): {
59
+ config: Record<string, unknown>
60
+ text: string
61
+ } | null => {
62
+ const path = opencodeConfigPath()
63
+ if (!existsSync(path)) return { config: {}, text: "{}" }
64
+ try {
65
+ const text = readFileSync(path, "utf8")
66
+ const errors: ParseError[] = []
67
+ const parsed: unknown = parse(text, errors, { allowTrailingComma: true })
68
+ if (errors.length > 0) return null
69
+ return parsed !== null && typeof parsed === "object"
70
+ ? { config: parsed as Record<string, unknown>, text }
71
+ : null
72
+ } catch {
73
+ return null
74
+ }
75
+ }
76
+
77
+ const pluginSpec = (entry: unknown): unknown =>
78
+ Array.isArray(entry) ? entry[0] : entry
79
+
80
+ export const isOpenCodePluginEnabled = (): boolean => {
81
+ const config = readOpenCodeConfig()
82
+ const plugins = config?.config["plugin"]
83
+ return (
84
+ Array.isArray(plugins) &&
85
+ plugins.some((entry) => pluginSpec(entry) === PLUGIN_SPEC)
86
+ )
87
+ }
88
+
89
+ export const enableOpenCodePlugin = (): boolean => {
90
+ const config = readOpenCodeConfig()
91
+ if (config === null) return false
92
+ const plugins = Array.isArray(config.config["plugin"])
93
+ ? config.config["plugin"]
94
+ : []
95
+ if (!plugins.some((entry) => pluginSpec(entry) === PLUGIN_SPEC)) {
96
+ plugins.push(PLUGIN_SPEC)
97
+ const updated = applyEdits(
98
+ config.text,
99
+ modify(config.text, ["plugin"], plugins, {
100
+ formattingOptions: { insertSpaces: true, tabSize: 2 },
101
+ }),
102
+ )
103
+ const path = opencodeConfigPath()
104
+ mkdirSync(opencodeConfigDir(), { recursive: true, mode: 0o700 })
105
+ writeFileSync(path, updated, {
106
+ encoding: "utf8",
107
+ mode: 0o600,
108
+ })
109
+ }
110
+ return true
111
+ }
112
+
113
+ export const codingAgentsPluginSource = `// ${PLUGIN_MARKER}
114
+ // Managed by Sireno Deck. Re-running setup replaces this file.
115
+ import { mkdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
116
+ import { homedir } from "node:os";
117
+ import { join } from "node:path";
118
+
119
+ const dir = join(process.env.XDG_STATE_HOME || join(homedir(), ".local", "state"), "sirenodeck", "coding-agents");
120
+ const file = join(dir, "opencode-" + process.pid + ".json");
121
+ let currentState = "idle";
122
+ let currentSessionID;
123
+ const write = (state, sessionID) => {
124
+ currentState = state;
125
+ if (sessionID) currentSessionID = sessionID;
126
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
127
+ const tmp = file + ".tmp";
128
+ writeFileSync(tmp, JSON.stringify({ pid: process.pid, cwd: process.cwd(), state: currentState, sessionID: currentSessionID, updatedAt: Date.now() }), { mode: 0o600 });
129
+ renameSync(tmp, file);
130
+ };
131
+ write("idle");
132
+
133
+ export const SirenoDeckAgentStatePlugin = async () => ({
134
+ event: async ({ event }) => {
135
+ const p = event?.properties || {};
136
+ const sessionID = p.sessionID;
137
+ switch (event?.type) {
138
+ case "session.status":
139
+ write(p.status?.type === "idle" ? "idle" : p.status?.type === "retry" ? "waiting" : "running", sessionID);
140
+ break;
141
+ case "session.idle": write("idle", sessionID); break;
142
+ case "permission.asked":
143
+ case "question.asked": write("waiting_for_human", sessionID); break;
144
+ case "session.error": write("error", sessionID); break;
145
+ }
146
+ },
147
+ });
148
+
149
+ process.once("exit", () => { try { unlinkSync(file); } catch {} });
150
+ const heartbeat = setInterval(() => write(currentState, currentSessionID), 5000);
151
+ process.once("exit", () => clearInterval(heartbeat));
152
+ `
153
+
154
+ export const installOpenCodePlugin = (): string => {
155
+ const path = opencodePluginPath()
156
+ mkdirSync(pluginDir(), { recursive: true, mode: 0o700 })
157
+ writeFileSync(path, codingAgentsPluginSource, {
158
+ encoding: "utf8",
159
+ mode: 0o600,
160
+ })
161
+ return path
162
+ }
163
+
164
+ // ---------------------------------------------------------------------------
165
+ // Claude Code
166
+ //
167
+ // ponytail: OpenCode needs a plugin injected into its own config. Claude Code
168
+ // has first-class hooks, so nothing has to be smuggled in — but its
169
+ // settings.json is a file the user owns and very likely already has hooks in
170
+ // (this machine carries three from other tools). Every edit below is surgical:
171
+ // jsonc-parser at the deepest path, ownership decided by a marker inside our
172
+ // own command string, and anything unmarked is never read, moved or rewritten.
173
+ // ---------------------------------------------------------------------------
174
+
175
+ const CLAUDE_HOOK_ID = `${CLAUDE_HOOK_MARKER}-v${CLAUDE_HOOK_VERSION}`
176
+
177
+ export const claudeConfigDir = (): string =>
178
+ process.env["CLAUDE_CONFIG_DIR"] ?? join(homedir(), ".claude")
179
+
180
+ export const claudeSettingsPath = (): string =>
181
+ join(claudeConfigDir(), "settings.json")
182
+
183
+ export const claudeHookPath = (): string =>
184
+ join(claudeConfigDir(), "hooks", CLAUDE_HOOK_FILENAME)
185
+
186
+ const claudeHookCommand = (): string =>
187
+ `node ${JSON.stringify(claudeHookPath())} $EVENT # ${CLAUDE_HOOK_ID}`
188
+
189
+ const commandFor = (event: string): string =>
190
+ claudeHookCommand().replace("$EVENT", event)
191
+
192
+ const isOurs = (entry: unknown): boolean => {
193
+ if (entry === null || typeof entry !== "object") return false
194
+ const hooks = (entry as { hooks?: unknown }).hooks
195
+ if (!Array.isArray(hooks)) return false
196
+ return hooks.some((h) => {
197
+ const cmd = (h as { command?: unknown }).command
198
+ return (
199
+ typeof cmd === "string" &&
200
+ (cmd.includes(CLAUDE_HOOK_MARKER) || cmd.includes(CLAUDE_HOOK_FILENAME))
201
+ )
202
+ })
203
+ }
204
+
205
+ export const isClaudeHookInstalled = (): boolean => {
206
+ try {
207
+ return readFileSync(claudeHookPath(), "utf8").includes(CLAUDE_HOOK_ID)
208
+ } catch {
209
+ return false
210
+ }
211
+ }
212
+
213
+ export const installClaudeHook = (): string => {
214
+ const path = claudeHookPath()
215
+ mkdirSync(join(claudeConfigDir(), "hooks"), { recursive: true, mode: 0o700 })
216
+ writeFileSync(path, claudeHookSource, { encoding: "utf8", mode: 0o700 })
217
+ return path
218
+ }
219
+
220
+ interface ClaudeSettings {
221
+ readonly text: string
222
+ readonly config: Record<string, unknown>
223
+ }
224
+
225
+ const readClaudeSettings = (): ClaudeSettings | null => {
226
+ const path = claudeSettingsPath()
227
+ let text: string
228
+ try {
229
+ text = readFileSync(path, "utf8")
230
+ } catch {
231
+ // No settings file yet — start from an empty object.
232
+ return { text: "{}", config: {} }
233
+ }
234
+ const errors: ParseError[] = []
235
+ const config = parse(text, errors, {
236
+ allowTrailingComma: true,
237
+ }) as Record<string, unknown>
238
+ // Refuse to touch a file we cannot faithfully re-emit.
239
+ if (errors.length > 0 || config === null || typeof config !== "object") {
240
+ return null
241
+ }
242
+ return { text, config }
243
+ }
244
+
245
+ const hookEntriesFor = (event: string): unknown => {
246
+ const reg = CLAUDE_HOOK_REGISTRATIONS.find((r) => r.event === event)
247
+ return {
248
+ ...(reg?.matcher !== undefined ? { matcher: reg.matcher } : {}),
249
+ hooks: [{ type: "command", command: commandFor(event), timeout: 5 }],
250
+ }
251
+ }
252
+
253
+ export const isClaudeHookEnabled = (): boolean => {
254
+ const settings = readClaudeSettings()
255
+ if (settings === null) return false
256
+ const hooks = settings.config["hooks"]
257
+ if (hooks === null || typeof hooks !== "object") return false
258
+ return CLAUDE_HOOK_REGISTRATIONS.every((reg) => {
259
+ const list = (hooks as Record<string, unknown>)[reg.event]
260
+ return Array.isArray(list) && list.some(isOurs)
261
+ })
262
+ }
263
+
264
+ const writeClaudeSettings = (text: string): void => {
265
+ const path = claudeSettingsPath()
266
+ mkdirSync(claudeConfigDir(), { recursive: true, mode: 0o700 })
267
+ const tmp = `${path}.sirenodeck.tmp`
268
+ writeFileSync(tmp, text, { encoding: "utf8", mode: 0o600 })
269
+ renameSync(tmp, path)
270
+ }
271
+
272
+ export const enableClaudeHooks = (): boolean => {
273
+ const settings = readClaudeSettings()
274
+ if (settings === null) return false
275
+
276
+ // One backup, before the first edit we ever make to this file.
277
+ const path = claudeSettingsPath()
278
+ const backup = `${path}.sirenodeck.bak`
279
+ if (existsSync(path) && !existsSync(backup)) {
280
+ try {
281
+ copyFileSync(path, backup)
282
+ } catch {
283
+ // Not fatal — the edits below are still surgical.
284
+ }
285
+ }
286
+
287
+ let text = settings.text
288
+ for (const reg of CLAUDE_HOOK_REGISTRATIONS) {
289
+ // Re-parse each round so indices stay correct as the document grows.
290
+ const current = parse(text, [], { allowTrailingComma: true }) as Record<
291
+ string,
292
+ unknown
293
+ >
294
+ const hooks = (current["hooks"] ?? {}) as Record<string, unknown>
295
+ const list = Array.isArray(hooks[reg.event])
296
+ ? (hooks[reg.event] as unknown[])
297
+ : []
298
+ const mine = list.findIndex(isOurs)
299
+ // Append at an explicit index, or replace our own entry in place. Sibling
300
+ // entries are never re-serialised, so other tools' hooks survive exactly.
301
+ const index = mine === -1 ? list.length : mine
302
+ text = applyEdits(
303
+ text,
304
+ modify(text, ["hooks", reg.event, index], hookEntriesFor(reg.event), {
305
+ formattingOptions: { insertSpaces: true, tabSize: 2 },
306
+ }),
307
+ )
308
+ }
309
+ writeClaudeSettings(text)
310
+ return true
311
+ }
312
+
313
+ export const removeClaudeHook = (): boolean => {
314
+ const settings = readClaudeSettings()
315
+ if (settings === null) return false
316
+ let text = settings.text
317
+ for (const reg of CLAUDE_HOOK_REGISTRATIONS) {
318
+ const current = parse(text, [], { allowTrailingComma: true }) as Record<
319
+ string,
320
+ unknown
321
+ >
322
+ const hooks = (current["hooks"] ?? {}) as Record<string, unknown>
323
+ const list = Array.isArray(hooks[reg.event])
324
+ ? (hooks[reg.event] as unknown[])
325
+ : []
326
+ const kept = list.filter((entry) => !isOurs(entry))
327
+ if (kept.length === list.length) continue
328
+ text = applyEdits(
329
+ text,
330
+ modify(text, ["hooks", reg.event], kept.length > 0 ? kept : undefined, {
331
+ formattingOptions: { insertSpaces: true, tabSize: 2 },
332
+ }),
333
+ )
334
+ }
335
+ writeClaudeSettings(text)
336
+ try {
337
+ unlinkSync(claudeHookPath())
338
+ } catch {
339
+ // Already gone.
340
+ }
341
+ return true
342
+ }
343
+
344
+ const onboardClaudeCode = async (options: {
345
+ readonly yes?: boolean
346
+ }): Promise<boolean> => {
347
+ if (isClaudeHookInstalled() && isClaudeHookEnabled()) return false
348
+ note(
349
+ "The coding-agents addon can show each Claude Code session and its live state.\n\n" +
350
+ "Sireno Deck will add hooks to your Claude Code settings. They report only local session state (id, directory, status) to files owned by your user; no prompts or code are captured. Existing hooks are left untouched.",
351
+ "Claude Code integration",
352
+ )
353
+ const answer =
354
+ options.yes === true
355
+ ? true
356
+ : await confirm({
357
+ message: "Add the Claude Code session-state hooks now?",
358
+ initialValue: true,
359
+ })
360
+ if (isCancel(answer) || !answer) return false
361
+ installClaudeHook()
362
+ if (!enableClaudeHooks()) {
363
+ note(
364
+ `Could not update ${claudeSettingsPath()} because it is not valid JSON. Nothing was changed.`,
365
+ "Claude Code integration",
366
+ )
367
+ return false
368
+ }
369
+ note(
370
+ `Installed ${claudeHookPath()}. Hooks apply to sessions started from now on; a backup of your settings is at ${claudeSettingsPath()}.sirenodeck.bak.`,
371
+ "Claude Code integration",
372
+ )
373
+ return true
374
+ }
375
+
376
+ export const onboardCodingAgents = async (
377
+ configPath: string,
378
+ options: { readonly nonInteractive?: boolean; readonly yes?: boolean } = {},
379
+ ): Promise<boolean> => {
380
+ if (!isCodingAgentsConfigured(configPath)) return false
381
+ // ponytail: this used to bail as soon as OpenCode was set up, which meant a
382
+ // user who had already onboarded OpenCode was never offered the Claude Code
383
+ // hooks at all. The two harnesses are independent; each decides for itself.
384
+ const openCodeDone = isOpenCodePluginInstalled() && isOpenCodePluginEnabled()
385
+ const claudeDone = isClaudeHookInstalled() && isClaudeHookEnabled()
386
+ if (openCodeDone && claudeDone) return false
387
+ // ponytail: with no TTY nobody can answer — @clack's confirm() renders the
388
+ // prompt and then blocks forever on a stdin that will never produce a
389
+ // keypress. `start` calls this BEFORE spawning the daemon, so a piped or
390
+ // redirected stdin (CI, a wrapper script, `| tee`) hung the whole start and
391
+ // surfaced as "port 52937 did not accept connections in 30s" — a daemon that
392
+ // was never launched. runFirstRunCheckIfNeeded already guards this way;
393
+ // keep the check here so every caller inherits it.
394
+ if (options.nonInteractive === true || process.stdin.isTTY !== true) {
395
+ return false
396
+ }
397
+ let changed = false
398
+
399
+ if (!openCodeDone) {
400
+ note(
401
+ "The coding-agents addon can show each OpenCode terminal instance and its live state.\n\n" +
402
+ "Sireno Deck will install a global OpenCode plugin. It reports only local process state to files owned by your user; no prompts or code are captured.",
403
+ "OpenCode integration",
404
+ )
405
+ const answer =
406
+ options.yes === true
407
+ ? true
408
+ : await confirm({
409
+ message: "Install the OpenCode instance-state plugin now?",
410
+ initialValue: true,
411
+ })
412
+ if (!isCancel(answer) && answer) {
413
+ installOpenCodePlugin()
414
+ if (enableOpenCodePlugin()) {
415
+ note(
416
+ `Installed and enabled ${opencodePluginPath()}. Restart OpenCode to activate it.`,
417
+ "OpenCode integration",
418
+ )
419
+ changed = true
420
+ } else {
421
+ note(
422
+ "Could not update OpenCode's opencode.json because it is invalid JSON.",
423
+ "OpenCode integration",
424
+ )
425
+ }
426
+ }
427
+ }
428
+
429
+ if (!claudeDone) {
430
+ changed = (await onboardClaudeCode(options)) || changed
431
+ }
432
+
433
+ return changed
434
+ }