@zerwiz/ymir 0.1.33 → 0.1.35

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 (930) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/apps/README.md +4 -0
  3. package/apps/hlidskjalf/README.md +68 -0
  4. package/apps/hlidskjalf/android/app/build.gradle +73 -0
  5. package/apps/hlidskjalf/android/app/capacitor.build.gradle +19 -0
  6. package/apps/hlidskjalf/android/app/proguard-rules.pro +21 -0
  7. package/apps/hlidskjalf/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java +26 -0
  8. package/apps/hlidskjalf/android/app/src/main/AndroidManifest.xml +41 -0
  9. package/apps/hlidskjalf/android/app/src/main/java/org/zerwiz/ymir/MainActivity.java +5 -0
  10. package/apps/hlidskjalf/android/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
  11. package/apps/hlidskjalf/android/app/src/main/res/drawable/splash.png +0 -0
  12. package/apps/hlidskjalf/android/app/src/main/res/drawable-land-hdpi/splash.png +0 -0
  13. package/apps/hlidskjalf/android/app/src/main/res/drawable-land-mdpi/splash.png +0 -0
  14. package/apps/hlidskjalf/android/app/src/main/res/drawable-land-xhdpi/splash.png +0 -0
  15. package/apps/hlidskjalf/android/app/src/main/res/drawable-land-xxhdpi/splash.png +0 -0
  16. package/apps/hlidskjalf/android/app/src/main/res/drawable-land-xxxhdpi/splash.png +0 -0
  17. package/apps/hlidskjalf/android/app/src/main/res/drawable-port-hdpi/splash.png +0 -0
  18. package/apps/hlidskjalf/android/app/src/main/res/drawable-port-mdpi/splash.png +0 -0
  19. package/apps/hlidskjalf/android/app/src/main/res/drawable-port-xhdpi/splash.png +0 -0
  20. package/apps/hlidskjalf/android/app/src/main/res/drawable-port-xxhdpi/splash.png +0 -0
  21. package/apps/hlidskjalf/android/app/src/main/res/drawable-port-xxxhdpi/splash.png +0 -0
  22. package/apps/hlidskjalf/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +34 -0
  23. package/apps/hlidskjalf/android/app/src/main/res/layout/activity_main.xml +12 -0
  24. package/apps/hlidskjalf/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  25. package/apps/hlidskjalf/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
  26. package/apps/hlidskjalf/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  27. package/apps/hlidskjalf/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png +0 -0
  28. package/apps/hlidskjalf/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  29. package/apps/hlidskjalf/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  30. package/apps/hlidskjalf/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png +0 -0
  31. package/apps/hlidskjalf/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  32. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  33. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
  34. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  35. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  36. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
  37. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  38. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  39. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
  40. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  41. package/apps/hlidskjalf/android/app/src/main/res/values/ic_launcher_background.xml +4 -0
  42. package/apps/hlidskjalf/android/app/src/main/res/values/strings.xml +7 -0
  43. package/apps/hlidskjalf/android/app/src/main/res/values/styles.xml +22 -0
  44. package/apps/hlidskjalf/android/app/src/main/res/xml/file_paths.xml +5 -0
  45. package/apps/hlidskjalf/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java +18 -0
  46. package/apps/hlidskjalf/android/build.gradle +29 -0
  47. package/apps/hlidskjalf/android/capacitor.settings.gradle +3 -0
  48. package/apps/hlidskjalf/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  49. package/apps/hlidskjalf/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  50. package/apps/hlidskjalf/android/gradle.properties +22 -0
  51. package/apps/hlidskjalf/android/gradlew +248 -0
  52. package/apps/hlidskjalf/android/gradlew.bat +92 -0
  53. package/apps/hlidskjalf/android/settings.gradle +5 -0
  54. package/apps/hlidskjalf/android/variables.gradle +16 -0
  55. package/apps/hlidskjalf/capacitor.config.ts +26 -0
  56. package/apps/hlidskjalf/dist/apple-touch-icon.png +0 -0
  57. package/apps/hlidskjalf/dist/assets/index-BWmNIyaG.css +1 -0
  58. package/apps/hlidskjalf/dist/assets/index-k7QEfNto.js +88 -0
  59. package/apps/hlidskjalf/dist/assets/index-k7QEfNto.js.map +1 -0
  60. package/apps/hlidskjalf/dist/icon.svg +7 -0
  61. package/apps/hlidskjalf/dist/index.html +64 -0
  62. package/apps/hlidskjalf/dist/manifest.webmanifest +31 -0
  63. package/apps/hlidskjalf/dist/og.jpg +0 -0
  64. package/apps/hlidskjalf/dist/og.png +0 -0
  65. package/apps/hlidskjalf/dist/og.svg +64 -0
  66. package/apps/hlidskjalf/dist/sw.js +39 -0
  67. package/apps/hlidskjalf/dist/ymir-icon.png +0 -0
  68. package/apps/hlidskjalf/dist/ymir-mark.svg +62 -0
  69. package/apps/hlidskjalf/electron/icon.png +0 -0
  70. package/apps/hlidskjalf/electron/main.cjs +236 -0
  71. package/apps/hlidskjalf/electron/preload.cjs +8 -0
  72. package/apps/hlidskjalf/electron/smidja-icon.png +0 -0
  73. package/apps/hlidskjalf/electron/ymir-hlidskjalf.desktop.in +12 -0
  74. package/apps/hlidskjalf/electron/ymir-smidja.desktop.in +12 -0
  75. package/apps/hlidskjalf/index.html +63 -0
  76. package/apps/hlidskjalf/package-lock.json +5398 -0
  77. package/apps/hlidskjalf/package.json +55 -0
  78. package/apps/hlidskjalf/public/apple-touch-icon.png +0 -0
  79. package/apps/hlidskjalf/public/icon.svg +7 -0
  80. package/apps/hlidskjalf/public/manifest.webmanifest +31 -0
  81. package/apps/hlidskjalf/public/og.jpg +0 -0
  82. package/apps/hlidskjalf/public/og.png +0 -0
  83. package/apps/hlidskjalf/public/og.svg +64 -0
  84. package/apps/hlidskjalf/public/sw.js +39 -0
  85. package/apps/hlidskjalf/public/ymir-icon.png +0 -0
  86. package/apps/hlidskjalf/public/ymir-mark.svg +62 -0
  87. package/apps/hlidskjalf/server/accounts.ts +159 -0
  88. package/apps/hlidskjalf/server/index.ts +2045 -0
  89. package/apps/hlidskjalf/src/app/App.tsx +116 -0
  90. package/apps/hlidskjalf/src/app/BottomStream.tsx +128 -0
  91. package/apps/hlidskjalf/src/app/MobileNav.tsx +87 -0
  92. package/apps/hlidskjalf/src/app/Rail.tsx +106 -0
  93. package/apps/hlidskjalf/src/app/Shell.tsx +135 -0
  94. package/apps/hlidskjalf/src/app/Topbar.tsx +130 -0
  95. package/apps/hlidskjalf/src/components/AccentPicker.tsx +207 -0
  96. package/apps/hlidskjalf/src/components/AccountMenu.tsx +115 -0
  97. package/apps/hlidskjalf/src/components/AgentCard.tsx +75 -0
  98. package/apps/hlidskjalf/src/components/EmberBackground.tsx +36 -0
  99. package/apps/hlidskjalf/src/components/Emblem.tsx +48 -0
  100. package/apps/hlidskjalf/src/components/Halls.tsx +155 -0
  101. package/apps/hlidskjalf/src/components/LoginModal.tsx +329 -0
  102. package/apps/hlidskjalf/src/components/Markdown.tsx +10 -0
  103. package/apps/hlidskjalf/src/components/MetricTile.tsx +40 -0
  104. package/apps/hlidskjalf/src/components/ModelPicker.tsx +100 -0
  105. package/apps/hlidskjalf/src/components/Overlay.tsx +134 -0
  106. package/apps/hlidskjalf/src/components/PRCard.tsx +154 -0
  107. package/apps/hlidskjalf/src/components/RecallPanel.tsx +116 -0
  108. package/apps/hlidskjalf/src/components/RuneTag.tsx +23 -0
  109. package/apps/hlidskjalf/src/components/Status.tsx +35 -0
  110. package/apps/hlidskjalf/src/components/TaskChip.tsx +23 -0
  111. package/apps/hlidskjalf/src/components/TraceRow.tsx +28 -0
  112. package/apps/hlidskjalf/src/data/lore.ts +127 -0
  113. package/apps/hlidskjalf/src/data/metadata.ts +147 -0
  114. package/apps/hlidskjalf/src/data/mythology.ts +95 -0
  115. package/apps/hlidskjalf/src/data/realms.ts +167 -0
  116. package/apps/hlidskjalf/src/gates/Cron.tsx +77 -0
  117. package/apps/hlidskjalf/src/gates/Decisions.tsx +67 -0
  118. package/apps/hlidskjalf/src/gates/Files.tsx +138 -0
  119. package/apps/hlidskjalf/src/gates/Fleet.tsx +184 -0
  120. package/apps/hlidskjalf/src/gates/Forge.tsx +486 -0
  121. package/apps/hlidskjalf/src/gates/OmniChat.tsx +217 -0
  122. package/apps/hlidskjalf/src/gates/Processes.tsx +162 -0
  123. package/apps/hlidskjalf/src/gates/Profile.tsx +225 -0
  124. package/apps/hlidskjalf/src/gates/Reviews.tsx +33 -0
  125. package/apps/hlidskjalf/src/gates/Runes.tsx +116 -0
  126. package/apps/hlidskjalf/src/gates/Runtime.tsx +106 -0
  127. package/apps/hlidskjalf/src/gates/Sessions.tsx +109 -0
  128. package/apps/hlidskjalf/src/gates/Stats.tsx +268 -0
  129. package/apps/hlidskjalf/src/gates/Tasks.tsx +173 -0
  130. package/apps/hlidskjalf/src/gates/Trace.tsx +155 -0
  131. package/apps/hlidskjalf/src/gates/Well.tsx +154 -0
  132. package/apps/hlidskjalf/src/gates/Worktrees.tsx +48 -0
  133. package/apps/hlidskjalf/src/hooks/useTenantDef.ts +13 -0
  134. package/apps/hlidskjalf/src/main.tsx +31 -0
  135. package/apps/hlidskjalf/src/services/api.ts +404 -0
  136. package/apps/hlidskjalf/src/services/auth.ts +107 -0
  137. package/apps/hlidskjalf/src/services/stream.ts +35 -0
  138. package/apps/hlidskjalf/src/state/store.ts +648 -0
  139. package/apps/hlidskjalf/src/state/ui.ts +62 -0
  140. package/apps/hlidskjalf/src/styles/auth.css +485 -0
  141. package/apps/hlidskjalf/src/styles/components.css +1259 -0
  142. package/apps/hlidskjalf/src/styles/forge.css +115 -0
  143. package/apps/hlidskjalf/src/styles/global.css +167 -0
  144. package/apps/hlidskjalf/src/styles/menus.css +175 -0
  145. package/apps/hlidskjalf/src/styles/mobile.css +298 -0
  146. package/apps/hlidskjalf/src/styles/overlays.css +205 -0
  147. package/apps/hlidskjalf/src/styles/shell.css +641 -0
  148. package/apps/hlidskjalf/src/types.ts +251 -0
  149. package/apps/hlidskjalf/src/vite-env.d.ts +9 -0
  150. package/apps/hlidskjalf/static/landing.md +6 -0
  151. package/apps/hlidskjalf/tsconfig.json +25 -0
  152. package/apps/hlidskjalf/vite.config.ts +49 -0
  153. package/apps/hlidskjalf-mobile/App.tsx +93 -0
  154. package/apps/hlidskjalf-mobile/app.json +13 -0
  155. package/apps/hlidskjalf-mobile/index.ts +4 -0
  156. package/apps/hlidskjalf-mobile/package.json +35 -0
  157. package/apps/hlidskjalf-mobile/public/icon.svg +7 -0
  158. package/apps/hlidskjalf-mobile/tsconfig.json +4 -0
  159. package/apps/odrerir/.astro/content.d.ts +159 -0
  160. package/apps/odrerir/.astro/dev.json +24 -0
  161. package/apps/odrerir/.astro/settings.json +5 -0
  162. package/apps/odrerir/.astro/types.d.ts +2 -0
  163. package/apps/odrerir/astro.config.mjs +79 -0
  164. package/apps/odrerir/dist/apple-touch-icon.png +0 -0
  165. package/apps/odrerir/dist/favicon-192.png +0 -0
  166. package/apps/odrerir/dist/favicon-32.png +0 -0
  167. package/apps/odrerir/dist/favicon-512.png +0 -0
  168. package/apps/odrerir/dist/favicon.svg +53 -0
  169. package/apps/odrerir/dist/icon.svg +7 -0
  170. package/apps/odrerir/dist/index.html +837 -0
  171. package/apps/odrerir/dist/og-square.png +0 -0
  172. package/apps/odrerir/dist/og-story.png +0 -0
  173. package/apps/odrerir/dist/og.png +0 -0
  174. package/apps/odrerir/dist/safari-pinned-tab.svg +7 -0
  175. package/apps/odrerir/dist/site.webmanifest +16 -0
  176. package/apps/odrerir/dist/styles/cloth.css +112 -0
  177. package/apps/odrerir/dist/styles/livehall.css +278 -0
  178. package/apps/odrerir/electron/icon.png +0 -0
  179. package/apps/odrerir/electron/main.cjs +183 -0
  180. package/apps/odrerir/electron/ymir-odrerir.desktop.in +12 -0
  181. package/apps/odrerir/package-lock.json +4195 -0
  182. package/apps/odrerir/package.json +30 -0
  183. package/apps/odrerir/public/apple-touch-icon.png +0 -0
  184. package/apps/odrerir/public/favicon-192.png +0 -0
  185. package/apps/odrerir/public/favicon-32.png +0 -0
  186. package/apps/odrerir/public/favicon-512.png +0 -0
  187. package/apps/odrerir/public/favicon.svg +53 -0
  188. package/apps/odrerir/public/icon.svg +7 -0
  189. package/apps/odrerir/public/og-square.png +0 -0
  190. package/apps/odrerir/public/og-story.png +0 -0
  191. package/apps/odrerir/public/og.png +0 -0
  192. package/apps/odrerir/public/safari-pinned-tab.svg +7 -0
  193. package/apps/odrerir/public/site.webmanifest +16 -0
  194. package/apps/odrerir/scripts/extract-cloth.mjs +101 -0
  195. package/apps/odrerir/src/index.html +837 -0
  196. package/apps/odrerir/src/styles/cloth.css +112 -0
  197. package/apps/odrerir/src/styles/livehall.css +278 -0
  198. package/apps/sessrumnir/.github/ISSUE_TEMPLATE/bug_report.yml +99 -0
  199. package/apps/sessrumnir/.github/ISSUE_TEMPLATE/config.yml +8 -0
  200. package/apps/sessrumnir/.github/ISSUE_TEMPLATE/feature_request.yml +46 -0
  201. package/apps/sessrumnir/.github/PULL_REQUEST_TEMPLATE.md +56 -0
  202. package/apps/sessrumnir/.github/SUPPORT.md +18 -0
  203. package/apps/sessrumnir/.github/workflows/build.yml +133 -0
  204. package/apps/sessrumnir/AGENTS.md +540 -0
  205. package/apps/sessrumnir/CLA.md +63 -0
  206. package/apps/sessrumnir/CONTRIBUTING.md +233 -0
  207. package/apps/sessrumnir/LICENSE +200 -0
  208. package/apps/sessrumnir/NOTICE +33 -0
  209. package/apps/sessrumnir/README.md +329 -0
  210. package/apps/sessrumnir/bin/sessrumnir-web.sh +117 -0
  211. package/apps/sessrumnir/bin/sessrumnir.js +169 -0
  212. package/apps/sessrumnir/docs/CNAME +1 -0
  213. package/apps/sessrumnir/docs/favicon.ico +0 -0
  214. package/apps/sessrumnir/docs/icon-512.png +0 -0
  215. package/apps/sessrumnir/docs/index.html +441 -0
  216. package/apps/sessrumnir/docs/robots.txt +4 -0
  217. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_181929.png +0 -0
  218. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182005.png +0 -0
  219. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182020.png +0 -0
  220. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182039.png +0 -0
  221. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182117.png +0 -0
  222. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182225.png +0 -0
  223. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182248.png +0 -0
  224. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182322.png +0 -0
  225. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182340.png +0 -0
  226. package/apps/sessrumnir/docs/sitemap.xml +9 -0
  227. package/apps/sessrumnir/electron.vite.config.ts +49 -0
  228. package/apps/sessrumnir/eslint.config.mjs +75 -0
  229. package/apps/sessrumnir/i18next.config.ts +33 -0
  230. package/apps/sessrumnir/install.sh +144 -0
  231. package/apps/sessrumnir/out/main/index.js +10881 -0
  232. package/apps/sessrumnir/out/preload/index.js +722 -0
  233. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-400-normal-C8QS47vb.woff2 +0 -0
  234. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-400-normal-D3EsxgFc.woff +0 -0
  235. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-500-normal-B7dJQtg-.woff +0 -0
  236. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-500-normal-BLlg2W5x.woff2 +0 -0
  237. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-600-normal-BcIsDXzC.woff2 +0 -0
  238. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-600-normal-CBymlcNF.woff +0 -0
  239. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-400-normal-Bgrpe4p1.woff +0 -0
  240. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-400-normal-BlcaxZtM.woff2 +0 -0
  241. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-500-normal-CdQuyvtc.woff +0 -0
  242. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-500-normal-pZw22qtS.woff2 +0 -0
  243. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-600-normal-Dr-wLyOm.woff +0 -0
  244. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-600-normal-dLR2-a_v.woff2 +0 -0
  245. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-400-normal-BGH8Vunh.woff2 +0 -0
  246. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-400-normal-C3_-2Ua-.woff +0 -0
  247. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-500-normal-Dj3SQ6fR.woff +0 -0
  248. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-500-normal-EBdSCOD3.woff2 +0 -0
  249. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-600-normal-B254Xisw.woff2 +0 -0
  250. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-600-normal-DUW-TI76.woff +0 -0
  251. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-400-normal-DuQ88yz3.woff2 +0 -0
  252. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-400-normal-DuXFa1Dr.woff +0 -0
  253. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-500-normal-AH9qog1s.woff2 +0 -0
  254. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-500-normal-DAuUCO41.woff +0 -0
  255. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-600-normal-CD-dlDpD.woff +0 -0
  256. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-600-normal-D3T6BBXG.woff2 +0 -0
  257. package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-400-normal-C-RiYxEf.woff2 +0 -0
  258. package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-400-normal-DmUuA7Y2.woff +0 -0
  259. package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-500-normal-DsPuwQHi.woff2 +0 -0
  260. package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-500-normal-tGBW_mI7.woff +0 -0
  261. package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-600-normal-Dar_cCT6.woff2 +0 -0
  262. package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-600-normal-GIs8CF2x.woff +0 -0
  263. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-300-normal-Ba-HN6uq.woff +0 -0
  264. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-300-normal-ByB2hQUF.woff2 +0 -0
  265. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
  266. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
  267. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
  268. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
  269. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-300-normal-B8O3uHbr.woff +0 -0
  270. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-300-normal-BbtBtHoB.woff2 +0 -0
  271. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
  272. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
  273. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
  274. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
  275. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-300-normal-BPU54Tw3.woff +0 -0
  276. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-300-normal-BRS-C4KA.woff2 +0 -0
  277. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  278. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  279. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  280. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  281. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-300-normal-BEtQ2wm2.woff2 +0 -0
  282. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-300-normal-CQbCaAQ4.woff +0 -0
  283. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
  284. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
  285. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
  286. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
  287. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-300-normal-B6fSpYYV.woff +0 -0
  288. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-300-normal-CpdbWO_2.woff2 +0 -0
  289. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
  290. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
  291. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
  292. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
  293. package/apps/sessrumnir/out/renderer/assets/index-BxqsRq2x.css +4858 -0
  294. package/apps/sessrumnir/out/renderer/assets/index-TaD69rFk.js +99435 -0
  295. package/apps/sessrumnir/out/renderer/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  296. package/apps/sessrumnir/out/renderer/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  297. package/apps/sessrumnir/out/renderer/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  298. package/apps/sessrumnir/out/renderer/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  299. package/apps/sessrumnir/out/renderer/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  300. package/apps/sessrumnir/out/renderer/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  301. package/apps/sessrumnir/out/renderer/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  302. package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  303. package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  304. package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  305. package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  306. package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  307. package/apps/sessrumnir/out/renderer/assets/newsreader-latin-ext-wght-italic-DFS986qL.woff2 +0 -0
  308. package/apps/sessrumnir/out/renderer/assets/newsreader-latin-ext-wght-normal-C-3rgBeH.woff2 +0 -0
  309. package/apps/sessrumnir/out/renderer/assets/newsreader-latin-wght-italic-Bxi8ein9.woff2 +0 -0
  310. package/apps/sessrumnir/out/renderer/assets/newsreader-latin-wght-normal-CCVVNp6i.woff2 +0 -0
  311. package/apps/sessrumnir/out/renderer/assets/newsreader-vietnamese-wght-italic-xAqemagA.woff2 +0 -0
  312. package/apps/sessrumnir/out/renderer/assets/newsreader-vietnamese-wght-normal-Czsa-EzN.woff2 +0 -0
  313. package/apps/sessrumnir/out/renderer/assets/openmoji-color-9qo_2DLG.woff2 +0 -0
  314. package/apps/sessrumnir/out/renderer/assets/pi-logo-B6SVnhuS.svg +7 -0
  315. package/apps/sessrumnir/out/renderer/index.html +19 -0
  316. package/apps/sessrumnir/out/renderer/theme-boot.js +30 -0
  317. package/apps/sessrumnir/package-lock.json +10636 -0
  318. package/apps/sessrumnir/package.json +183 -0
  319. package/apps/sessrumnir/public/apple-touch-icon.png +0 -0
  320. package/apps/sessrumnir/public/icon.svg +7 -0
  321. package/apps/sessrumnir/resources/icons/generate_icons.py +38 -0
  322. package/apps/sessrumnir/resources/icons/icon-128.png +0 -0
  323. package/apps/sessrumnir/resources/icons/icon-16.png +0 -0
  324. package/apps/sessrumnir/resources/icons/icon-256.png +0 -0
  325. package/apps/sessrumnir/resources/icons/icon-32.png +0 -0
  326. package/apps/sessrumnir/resources/icons/icon-48.png +0 -0
  327. package/apps/sessrumnir/resources/icons/icon-512.png +0 -0
  328. package/apps/sessrumnir/resources/icons/icon-64.png +0 -0
  329. package/apps/sessrumnir/resources/icons/icon.icns +0 -0
  330. package/apps/sessrumnir/resources/icons/icon.ico +0 -0
  331. package/apps/sessrumnir/resources/icons/icon.png +0 -0
  332. package/apps/sessrumnir/resources/icons/icon.svg +7 -0
  333. package/apps/sessrumnir/resources/linux/after-install.sh +23 -0
  334. package/apps/sessrumnir/resources/linux/after-remove.sh +18 -0
  335. package/apps/sessrumnir/resources/locales/en/translation.json +1735 -0
  336. package/apps/sessrumnir/resources/permission-prompt-text.test.ts +111 -0
  337. package/apps/sessrumnir/resources/permission-prompt-text.ts +84 -0
  338. package/apps/sessrumnir/resources/permission-rules.test.ts +351 -0
  339. package/apps/sessrumnir/resources/permission-rules.ts +293 -0
  340. package/apps/sessrumnir/resources/pi-desktop-permissions.ts +62 -0
  341. package/apps/sessrumnir/resources/ymir-sessrumnir.desktop.in +12 -0
  342. package/apps/sessrumnir/scripts/check-semantic-colors.mjs +57 -0
  343. package/apps/sessrumnir/src/main/activity-stats.test.ts +240 -0
  344. package/apps/sessrumnir/src/main/activity-stats.ts +596 -0
  345. package/apps/sessrumnir/src/main/agent-detection.test.ts +25 -0
  346. package/apps/sessrumnir/src/main/agent-detection.ts +114 -0
  347. package/apps/sessrumnir/src/main/app-data-paths.test.ts +90 -0
  348. package/apps/sessrumnir/src/main/app-data-paths.ts +95 -0
  349. package/apps/sessrumnir/src/main/app-log.test.ts +114 -0
  350. package/apps/sessrumnir/src/main/app-log.ts +210 -0
  351. package/apps/sessrumnir/src/main/archived-sessions.test.ts +50 -0
  352. package/apps/sessrumnir/src/main/archived-sessions.ts +95 -0
  353. package/apps/sessrumnir/src/main/attachment-reader.test.ts +56 -0
  354. package/apps/sessrumnir/src/main/attachment-reader.ts +52 -0
  355. package/apps/sessrumnir/src/main/auto-tag.test.ts +38 -0
  356. package/apps/sessrumnir/src/main/auto-tag.ts +75 -0
  357. package/apps/sessrumnir/src/main/autostart-linux.test.ts +56 -0
  358. package/apps/sessrumnir/src/main/autostart-linux.ts +58 -0
  359. package/apps/sessrumnir/src/main/cmd-escape.test.ts +424 -0
  360. package/apps/sessrumnir/src/main/cmd-escape.ts +186 -0
  361. package/apps/sessrumnir/src/main/council-manager.test.ts +167 -0
  362. package/apps/sessrumnir/src/main/council-manager.ts +265 -0
  363. package/apps/sessrumnir/src/main/diagnostics-report.test.ts +140 -0
  364. package/apps/sessrumnir/src/main/diagnostics-report.ts +91 -0
  365. package/apps/sessrumnir/src/main/diagnostics.ts +111 -0
  366. package/apps/sessrumnir/src/main/editor-guard.test.ts +59 -0
  367. package/apps/sessrumnir/src/main/editor-guard.ts +50 -0
  368. package/apps/sessrumnir/src/main/extension-ui-ipc.test.ts +299 -0
  369. package/apps/sessrumnir/src/main/extension-ui-ipc.ts +117 -0
  370. package/apps/sessrumnir/src/main/file-service.test.ts +279 -0
  371. package/apps/sessrumnir/src/main/file-service.ts +652 -0
  372. package/apps/sessrumnir/src/main/fs-errors.ts +42 -0
  373. package/apps/sessrumnir/src/main/get-messages-trim.test.ts +77 -0
  374. package/apps/sessrumnir/src/main/get-messages-trim.ts +97 -0
  375. package/apps/sessrumnir/src/main/git-conveyor.test.ts +305 -0
  376. package/apps/sessrumnir/src/main/git-conveyor.ts +338 -0
  377. package/apps/sessrumnir/src/main/git-worktree.test.ts +101 -0
  378. package/apps/sessrumnir/src/main/git-worktree.ts +201 -0
  379. package/apps/sessrumnir/src/main/i18n.ts +31 -0
  380. package/apps/sessrumnir/src/main/index.ts +619 -0
  381. package/apps/sessrumnir/src/main/ipc/active-engine.ts +15 -0
  382. package/apps/sessrumnir/src/main/ipc/context.ts +58 -0
  383. package/apps/sessrumnir/src/main/ipc/council-handlers.ts +126 -0
  384. package/apps/sessrumnir/src/main/ipc/diagnostics-handlers.ts +8 -0
  385. package/apps/sessrumnir/src/main/ipc/file-handlers.ts +96 -0
  386. package/apps/sessrumnir/src/main/ipc/git-conveyor-handlers.ts +55 -0
  387. package/apps/sessrumnir/src/main/ipc/model-handlers.ts +33 -0
  388. package/apps/sessrumnir/src/main/ipc/models-config-handlers.ts +80 -0
  389. package/apps/sessrumnir/src/main/ipc/notes-handlers.ts +65 -0
  390. package/apps/sessrumnir/src/main/ipc/package-handlers.ts +330 -0
  391. package/apps/sessrumnir/src/main/ipc/permission-rules-handlers.ts +223 -0
  392. package/apps/sessrumnir/src/main/ipc/pi-handlers.ts +136 -0
  393. package/apps/sessrumnir/src/main/ipc/pi-start-options.ts +150 -0
  394. package/apps/sessrumnir/src/main/ipc/run-pi-cli.ts +53 -0
  395. package/apps/sessrumnir/src/main/ipc/session-handlers.ts +491 -0
  396. package/apps/sessrumnir/src/main/ipc/settings.ts +120 -0
  397. package/apps/sessrumnir/src/main/ipc/skills-mcp-handlers.ts +192 -0
  398. package/apps/sessrumnir/src/main/ipc/system-handlers.ts +131 -0
  399. package/apps/sessrumnir/src/main/ipc/tag-handlers.ts +69 -0
  400. package/apps/sessrumnir/src/main/ipc/terminal-handlers.ts +41 -0
  401. package/apps/sessrumnir/src/main/ipc/theme-handlers.ts +131 -0
  402. package/apps/sessrumnir/src/main/ipc/update-handlers.ts +61 -0
  403. package/apps/sessrumnir/src/main/ipc/validation.ts +45 -0
  404. package/apps/sessrumnir/src/main/ipc/workflow-handlers.ts +108 -0
  405. package/apps/sessrumnir/src/main/ipc/workspace-activity-wiring.ts +179 -0
  406. package/apps/sessrumnir/src/main/ipc/workspace-handlers.ts +133 -0
  407. package/apps/sessrumnir/src/main/ipc-handlers.ts +98 -0
  408. package/apps/sessrumnir/src/main/map-concurrent.test.ts +80 -0
  409. package/apps/sessrumnir/src/main/map-concurrent.ts +30 -0
  410. package/apps/sessrumnir/src/main/models-file.test.ts +114 -0
  411. package/apps/sessrumnir/src/main/models-file.ts +89 -0
  412. package/apps/sessrumnir/src/main/notes-manager.test.ts +64 -0
  413. package/apps/sessrumnir/src/main/notes-manager.ts +183 -0
  414. package/apps/sessrumnir/src/main/notify-decision.test.ts +39 -0
  415. package/apps/sessrumnir/src/main/notify-decision.ts +25 -0
  416. package/apps/sessrumnir/src/main/omp-fork-points.test.ts +119 -0
  417. package/apps/sessrumnir/src/main/omp-fork-points.ts +105 -0
  418. package/apps/sessrumnir/src/main/omp-plugin-list.test.ts +94 -0
  419. package/apps/sessrumnir/src/main/omp-plugin-list.ts +103 -0
  420. package/apps/sessrumnir/src/main/package-catalog.test.ts +157 -0
  421. package/apps/sessrumnir/src/main/package-catalog.ts +166 -0
  422. package/apps/sessrumnir/src/main/package-updates.test.ts +149 -0
  423. package/apps/sessrumnir/src/main/package-updates.ts +185 -0
  424. package/apps/sessrumnir/src/main/path-authorization.test.ts +64 -0
  425. package/apps/sessrumnir/src/main/path-authorization.ts +31 -0
  426. package/apps/sessrumnir/src/main/pi-binary-resolution.test.ts +582 -0
  427. package/apps/sessrumnir/src/main/pi-binary-resolution.ts +649 -0
  428. package/apps/sessrumnir/src/main/pi-event-router.test.ts +388 -0
  429. package/apps/sessrumnir/src/main/pi-event-router.ts +290 -0
  430. package/apps/sessrumnir/src/main/pi-paths.test.ts +134 -0
  431. package/apps/sessrumnir/src/main/pi-paths.ts +74 -0
  432. package/apps/sessrumnir/src/main/pi-rpc-manager.test.ts +467 -0
  433. package/apps/sessrumnir/src/main/pi-rpc-manager.ts +1319 -0
  434. package/apps/sessrumnir/src/main/process-tree.test.ts +94 -0
  435. package/apps/sessrumnir/src/main/renderer-origin.test.ts +33 -0
  436. package/apps/sessrumnir/src/main/renderer-origin.ts +35 -0
  437. package/apps/sessrumnir/src/main/session-lineage-reader.test.ts +317 -0
  438. package/apps/sessrumnir/src/main/session-lineage-reader.ts +111 -0
  439. package/apps/sessrumnir/src/main/session-metadata.test.ts +508 -0
  440. package/apps/sessrumnir/src/main/session-metadata.ts +526 -0
  441. package/apps/sessrumnir/src/main/session-name.test.ts +71 -0
  442. package/apps/sessrumnir/src/main/session-name.ts +69 -0
  443. package/apps/sessrumnir/src/main/session-paths.test.ts +110 -0
  444. package/apps/sessrumnir/src/main/session-paths.ts +88 -0
  445. package/apps/sessrumnir/src/main/session-tags.test.ts +61 -0
  446. package/apps/sessrumnir/src/main/session-tags.ts +253 -0
  447. package/apps/sessrumnir/src/main/session-trash.test.ts +66 -0
  448. package/apps/sessrumnir/src/main/session-trash.ts +65 -0
  449. package/apps/sessrumnir/src/main/settings-logic.ts +66 -0
  450. package/apps/sessrumnir/src/main/skills-discovery.test.ts +149 -0
  451. package/apps/sessrumnir/src/main/skills-discovery.ts +179 -0
  452. package/apps/sessrumnir/src/main/startup-launch.ts +63 -0
  453. package/apps/sessrumnir/src/main/terminal-service.ts +96 -0
  454. package/apps/sessrumnir/src/main/theme-store.test.ts +440 -0
  455. package/apps/sessrumnir/src/main/theme-store.ts +495 -0
  456. package/apps/sessrumnir/src/main/tray-decision.test.ts +46 -0
  457. package/apps/sessrumnir/src/main/tray-decision.ts +49 -0
  458. package/apps/sessrumnir/src/main/tray-manager.ts +220 -0
  459. package/apps/sessrumnir/src/main/tsconfig.json +20 -0
  460. package/apps/sessrumnir/src/main/window-background.test.ts +29 -0
  461. package/apps/sessrumnir/src/main/window-background.ts +16 -0
  462. package/apps/sessrumnir/src/main/workflow-discovery.test.ts +252 -0
  463. package/apps/sessrumnir/src/main/workflow-monitor.test.ts +371 -0
  464. package/apps/sessrumnir/src/main/workflow-monitor.ts +1020 -0
  465. package/apps/sessrumnir/src/main/workspace-activity.test.ts +227 -0
  466. package/apps/sessrumnir/src/main/workspace-activity.ts +215 -0
  467. package/apps/sessrumnir/src/main/workspace-manager.test.ts +680 -0
  468. package/apps/sessrumnir/src/main/workspace-manager.ts +1199 -0
  469. package/apps/sessrumnir/src/main/workspace-trust.test.ts +78 -0
  470. package/apps/sessrumnir/src/main/workspace-trust.ts +84 -0
  471. package/apps/sessrumnir/src/preload/index.ts +358 -0
  472. package/apps/sessrumnir/src/preload/tsconfig.json +20 -0
  473. package/apps/sessrumnir/src/renderer/index.html +18 -0
  474. package/apps/sessrumnir/src/renderer/public/theme-boot.js +30 -0
  475. package/apps/sessrumnir/src/renderer/src/app.tsx +194 -0
  476. package/apps/sessrumnir/src/renderer/src/assets/openmoji-color.woff2 +0 -0
  477. package/apps/sessrumnir/src/renderer/src/assets/pi-logo.svg +7 -0
  478. package/apps/sessrumnir/src/renderer/src/assets/ymir-mark.svg +55 -0
  479. package/apps/sessrumnir/src/renderer/src/assets.d.ts +7 -0
  480. package/apps/sessrumnir/src/renderer/src/components/chat-code-highlight.ts +239 -0
  481. package/apps/sessrumnir/src/renderer/src/components/chat-file-link.ts +140 -0
  482. package/apps/sessrumnir/src/renderer/src/components/chat-input.tsx +729 -0
  483. package/apps/sessrumnir/src/renderer/src/components/chat-panel-widths.test.ts +175 -0
  484. package/apps/sessrumnir/src/renderer/src/components/chat-panel-widths.ts +100 -0
  485. package/apps/sessrumnir/src/renderer/src/components/chat-panel.tsx +523 -0
  486. package/apps/sessrumnir/src/renderer/src/components/chat-project-picker.tsx +193 -0
  487. package/apps/sessrumnir/src/renderer/src/components/chat-search.tsx +251 -0
  488. package/apps/sessrumnir/src/renderer/src/components/code-editor-highlight.ts +85 -0
  489. package/apps/sessrumnir/src/renderer/src/components/code-editor-language.test.ts +19 -0
  490. package/apps/sessrumnir/src/renderer/src/components/code-editor-language.ts +88 -0
  491. package/apps/sessrumnir/src/renderer/src/components/code-editor.tsx +128 -0
  492. package/apps/sessrumnir/src/renderer/src/components/command-palette.tsx +315 -0
  493. package/apps/sessrumnir/src/renderer/src/components/command-results.tsx +80 -0
  494. package/apps/sessrumnir/src/renderer/src/components/composer-permission-menu.tsx +91 -0
  495. package/apps/sessrumnir/src/renderer/src/components/context-menu.tsx +457 -0
  496. package/apps/sessrumnir/src/renderer/src/components/copy-button.tsx +36 -0
  497. package/apps/sessrumnir/src/renderer/src/components/council-panels.tsx +193 -0
  498. package/apps/sessrumnir/src/renderer/src/components/custom-models-editor.tsx +330 -0
  499. package/apps/sessrumnir/src/renderer/src/components/diagnostics-panel.tsx +380 -0
  500. package/apps/sessrumnir/src/renderer/src/components/diff-viewer.tsx +318 -0
  501. package/apps/sessrumnir/src/renderer/src/components/ember-background.tsx +113 -0
  502. package/apps/sessrumnir/src/renderer/src/components/error-boundary.tsx +38 -0
  503. package/apps/sessrumnir/src/renderer/src/components/extension-ui-dialog-helpers.test.ts +43 -0
  504. package/apps/sessrumnir/src/renderer/src/components/extension-ui-dialog-helpers.ts +51 -0
  505. package/apps/sessrumnir/src/renderer/src/components/extension-ui-dialog.tsx +486 -0
  506. package/apps/sessrumnir/src/renderer/src/components/file-tree.tsx +784 -0
  507. package/apps/sessrumnir/src/renderer/src/components/git-conveyor-actions.tsx +246 -0
  508. package/apps/sessrumnir/src/renderer/src/components/home-screen.tsx +396 -0
  509. package/apps/sessrumnir/src/renderer/src/components/image-viewer.tsx +100 -0
  510. package/apps/sessrumnir/src/renderer/src/components/line-numbered-code.tsx +58 -0
  511. package/apps/sessrumnir/src/renderer/src/components/markdown-renderer.tsx +237 -0
  512. package/apps/sessrumnir/src/renderer/src/components/message-bubble.tsx +1006 -0
  513. package/apps/sessrumnir/src/renderer/src/components/mission-control.tsx +341 -0
  514. package/apps/sessrumnir/src/renderer/src/components/model-selector.tsx +219 -0
  515. package/apps/sessrumnir/src/renderer/src/components/note-picker.tsx +155 -0
  516. package/apps/sessrumnir/src/renderer/src/components/notes-panel.tsx +437 -0
  517. package/apps/sessrumnir/src/renderer/src/components/package-browser.tsx +604 -0
  518. package/apps/sessrumnir/src/renderer/src/components/permission-mode.test.ts +22 -0
  519. package/apps/sessrumnir/src/renderer/src/components/permission-mode.ts +49 -0
  520. package/apps/sessrumnir/src/renderer/src/components/permission-rules-editor-helpers.test.ts +44 -0
  521. package/apps/sessrumnir/src/renderer/src/components/permission-rules-editor-helpers.ts +35 -0
  522. package/apps/sessrumnir/src/renderer/src/components/permission-rules-editor.tsx +226 -0
  523. package/apps/sessrumnir/src/renderer/src/components/permission-selector.tsx +104 -0
  524. package/apps/sessrumnir/src/renderer/src/components/resize-handle.tsx +51 -0
  525. package/apps/sessrumnir/src/renderer/src/components/review-rail.tsx +194 -0
  526. package/apps/sessrumnir/src/renderer/src/components/session-menu-position.test.ts +22 -0
  527. package/apps/sessrumnir/src/renderer/src/components/session-menu-position.ts +34 -0
  528. package/apps/sessrumnir/src/renderer/src/components/session-panel.tsx +711 -0
  529. package/apps/sessrumnir/src/renderer/src/components/session-runtime-indicator.tsx +43 -0
  530. package/apps/sessrumnir/src/renderer/src/components/settings-panel.tsx +1360 -0
  531. package/apps/sessrumnir/src/renderer/src/components/sidebar-activity.test.ts +47 -0
  532. package/apps/sessrumnir/src/renderer/src/components/sidebar-activity.ts +76 -0
  533. package/apps/sessrumnir/src/renderer/src/components/sidebar-session-labels.test.ts +73 -0
  534. package/apps/sessrumnir/src/renderer/src/components/sidebar-session-labels.ts +47 -0
  535. package/apps/sessrumnir/src/renderer/src/components/sidebar.tsx +1045 -0
  536. package/apps/sessrumnir/src/renderer/src/components/skills-panel.tsx +159 -0
  537. package/apps/sessrumnir/src/renderer/src/components/stats-panel.tsx +368 -0
  538. package/apps/sessrumnir/src/renderer/src/components/status-bar.tsx +274 -0
  539. package/apps/sessrumnir/src/renderer/src/components/status-popover.tsx +481 -0
  540. package/apps/sessrumnir/src/renderer/src/components/streaming-bubble.tsx +128 -0
  541. package/apps/sessrumnir/src/renderer/src/components/subagent-progress.tsx +254 -0
  542. package/apps/sessrumnir/src/renderer/src/components/task-launcher.tsx +168 -0
  543. package/apps/sessrumnir/src/renderer/src/components/terminal.tsx +203 -0
  544. package/apps/sessrumnir/src/renderer/src/components/theme-editor-helpers.test.ts +35 -0
  545. package/apps/sessrumnir/src/renderer/src/components/theme-editor-helpers.ts +35 -0
  546. package/apps/sessrumnir/src/renderer/src/components/theme-editor.tsx +440 -0
  547. package/apps/sessrumnir/src/renderer/src/components/theme-gallery.tsx +373 -0
  548. package/apps/sessrumnir/src/renderer/src/components/thinking-level-selector.tsx +85 -0
  549. package/apps/sessrumnir/src/renderer/src/components/timeline.tsx +289 -0
  550. package/apps/sessrumnir/src/renderer/src/components/tool-call-icon.ts +32 -0
  551. package/apps/sessrumnir/src/renderer/src/components/workflow-navigator.tsx +901 -0
  552. package/apps/sessrumnir/src/renderer/src/components/workspace-tabs.tsx +241 -0
  553. package/apps/sessrumnir/src/renderer/src/global.d.ts +8 -0
  554. package/apps/sessrumnir/src/renderer/src/hooks/use-folder-drop.ts +101 -0
  555. package/apps/sessrumnir/src/renderer/src/hooks.test.ts +88 -0
  556. package/apps/sessrumnir/src/renderer/src/hooks.ts +574 -0
  557. package/apps/sessrumnir/src/renderer/src/i18n.test.ts +54 -0
  558. package/apps/sessrumnir/src/renderer/src/i18n.ts +89 -0
  559. package/apps/sessrumnir/src/renderer/src/index.css +425 -0
  560. package/apps/sessrumnir/src/renderer/src/main.tsx +42 -0
  561. package/apps/sessrumnir/src/renderer/src/message-grouping.test.ts +422 -0
  562. package/apps/sessrumnir/src/renderer/src/message-grouping.ts +410 -0
  563. package/apps/sessrumnir/src/renderer/src/message-parsing.test.ts +86 -0
  564. package/apps/sessrumnir/src/renderer/src/message-parsing.ts +166 -0
  565. package/apps/sessrumnir/src/renderer/src/store-external-prompts.test.ts +161 -0
  566. package/apps/sessrumnir/src/renderer/src/store-session-transitions.test.ts +2090 -0
  567. package/apps/sessrumnir/src/renderer/src/store-timeline.test.ts +52 -0
  568. package/apps/sessrumnir/src/renderer/src/store-turn-error.test.ts +136 -0
  569. package/apps/sessrumnir/src/renderer/src/store.ts +3726 -0
  570. package/apps/sessrumnir/src/renderer/src/subagent-detection.test.ts +55 -0
  571. package/apps/sessrumnir/src/renderer/src/theme/engine.test.ts +29 -0
  572. package/apps/sessrumnir/src/renderer/src/theme/engine.ts +20 -0
  573. package/apps/sessrumnir/src/renderer/src/themes/breeze-claudius.json +69 -0
  574. package/apps/sessrumnir/src/renderer/src/themes/breeze-dark.json +69 -0
  575. package/apps/sessrumnir/src/renderer/src/themes/breeze-light.json +69 -0
  576. package/apps/sessrumnir/src/renderer/src/themes/builtin-themes.test.ts +95 -0
  577. package/apps/sessrumnir/src/renderer/src/themes/dark.json +46 -0
  578. package/apps/sessrumnir/src/renderer/src/themes/fensalir.json +82 -0
  579. package/apps/sessrumnir/src/renderer/src/themes/gruvbox.json +69 -0
  580. package/apps/sessrumnir/src/renderer/src/themes/index.ts +60 -0
  581. package/apps/sessrumnir/src/renderer/src/themes/light.json +46 -0
  582. package/apps/sessrumnir/src/renderer/src/themes/nord.json +69 -0
  583. package/apps/sessrumnir/src/renderer/src/themes/sessrumnir.json +82 -0
  584. package/apps/sessrumnir/src/renderer/src/utils/debounced-buffer.test.ts +81 -0
  585. package/apps/sessrumnir/src/renderer/src/utils/debounced-buffer.ts +57 -0
  586. package/apps/sessrumnir/src/renderer/src/utils/format-relative-time.test.ts +62 -0
  587. package/apps/sessrumnir/src/renderer/src/utils/format-relative-time.ts +28 -0
  588. package/apps/sessrumnir/src/renderer/src/utils/heatmap-grid.test.ts +57 -0
  589. package/apps/sessrumnir/src/renderer/src/utils/heatmap-grid.ts +41 -0
  590. package/apps/sessrumnir/src/renderer/src/utils/ipc-error.test.ts +22 -0
  591. package/apps/sessrumnir/src/renderer/src/utils/ipc-error.ts +12 -0
  592. package/apps/sessrumnir/src/renderer/src/utils/model-search.test.ts +63 -0
  593. package/apps/sessrumnir/src/renderer/src/utils/model-search.ts +20 -0
  594. package/apps/sessrumnir/src/renderer/src/utils/planning-prompt.test.ts +31 -0
  595. package/apps/sessrumnir/src/renderer/src/utils/planning-prompt.ts +27 -0
  596. package/apps/sessrumnir/src/renderer/src/utils/preview-load-error.test.ts +15 -0
  597. package/apps/sessrumnir/src/renderer/src/utils/preview-load-error.ts +22 -0
  598. package/apps/sessrumnir/src/renderer/src/utils/process-status-label.test.ts +13 -0
  599. package/apps/sessrumnir/src/renderer/src/utils/process-status-label.ts +19 -0
  600. package/apps/sessrumnir/src/renderer/src/utils/quick-switcher.test.ts +68 -0
  601. package/apps/sessrumnir/src/renderer/src/utils/quick-switcher.ts +52 -0
  602. package/apps/sessrumnir/src/renderer/src/utils/rank-file-results.test.ts +46 -0
  603. package/apps/sessrumnir/src/renderer/src/utils/rank-file-results.ts +23 -0
  604. package/apps/sessrumnir/src/renderer/src/utils/relative-time.tsx +25 -0
  605. package/apps/sessrumnir/src/renderer/src/utils/session-title.test.ts +98 -0
  606. package/apps/sessrumnir/src/renderer/src/utils/session-title.ts +58 -0
  607. package/apps/sessrumnir/src/renderer/src/utils/stale-guard.test.ts +29 -0
  608. package/apps/sessrumnir/src/renderer/src/utils/stale-guard.ts +20 -0
  609. package/apps/sessrumnir/src/renderer/src/utils/theme.test.ts +174 -0
  610. package/apps/sessrumnir/src/renderer/src/utils/theme.ts +159 -0
  611. package/apps/sessrumnir/src/renderer/src/utils/workflow-runs.test.ts +130 -0
  612. package/apps/sessrumnir/src/renderer/src/utils/workflow-runs.ts +84 -0
  613. package/apps/sessrumnir/src/renderer/tsconfig.json +24 -0
  614. package/apps/sessrumnir/src/shared/agent-engine-label.test.ts +45 -0
  615. package/apps/sessrumnir/src/shared/agent-engine-label.ts +53 -0
  616. package/apps/sessrumnir/src/shared/app-settings.test.ts +25 -0
  617. package/apps/sessrumnir/src/shared/app-settings.ts +23 -0
  618. package/apps/sessrumnir/src/shared/bridge-http.ts +341 -0
  619. package/apps/sessrumnir/src/shared/bridge.ts +337 -0
  620. package/apps/sessrumnir/src/shared/council-config.test.ts +300 -0
  621. package/apps/sessrumnir/src/shared/council-config.ts +356 -0
  622. package/apps/sessrumnir/src/shared/default-settings.ts +39 -0
  623. package/apps/sessrumnir/src/shared/folder-drop.test.ts +118 -0
  624. package/apps/sessrumnir/src/shared/folder-drop.ts +74 -0
  625. package/apps/sessrumnir/src/shared/fork-point.test.ts +30 -0
  626. package/apps/sessrumnir/src/shared/fork-point.ts +24 -0
  627. package/apps/sessrumnir/src/shared/i18n/i18next.d.ts +10 -0
  628. package/apps/sessrumnir/src/shared/i18n/index.test.ts +51 -0
  629. package/apps/sessrumnir/src/shared/i18n/index.ts +50 -0
  630. package/apps/sessrumnir/src/shared/i18n/languages.ts +12 -0
  631. package/apps/sessrumnir/src/shared/i18n/locale-checks.ts +98 -0
  632. package/apps/sessrumnir/src/shared/i18n/locales.test.ts +92 -0
  633. package/apps/sessrumnir/src/shared/i18n/pseudo.test.ts +39 -0
  634. package/apps/sessrumnir/src/shared/i18n/pseudo.ts +44 -0
  635. package/apps/sessrumnir/src/shared/i18n/resolve.test.ts +43 -0
  636. package/apps/sessrumnir/src/shared/i18n/resolve.ts +41 -0
  637. package/apps/sessrumnir/src/shared/i18n/resources.ts +11 -0
  638. package/apps/sessrumnir/src/shared/ipc-contracts.ts +1539 -0
  639. package/apps/sessrumnir/src/shared/models-config.test.ts +124 -0
  640. package/apps/sessrumnir/src/shared/models-config.ts +167 -0
  641. package/apps/sessrumnir/src/shared/package-filter.ts +19 -0
  642. package/apps/sessrumnir/src/shared/package-spec.test.ts +51 -0
  643. package/apps/sessrumnir/src/shared/package-spec.ts +25 -0
  644. package/apps/sessrumnir/src/shared/path-compare.test.ts +38 -0
  645. package/apps/sessrumnir/src/shared/path-compare.ts +76 -0
  646. package/apps/sessrumnir/src/shared/permission-mode.ts +8 -0
  647. package/apps/sessrumnir/src/shared/pi-command.test.ts +165 -0
  648. package/apps/sessrumnir/src/shared/pi-command.ts +139 -0
  649. package/apps/sessrumnir/src/shared/product-name.ts +7 -0
  650. package/apps/sessrumnir/src/shared/session-lineage.test.ts +103 -0
  651. package/apps/sessrumnir/src/shared/session-lineage.ts +50 -0
  652. package/apps/sessrumnir/src/shared/session-preview.test.ts +93 -0
  653. package/apps/sessrumnir/src/shared/session-preview.ts +56 -0
  654. package/apps/sessrumnir/src/shared/sidebar-width.test.ts +58 -0
  655. package/apps/sessrumnir/src/shared/sidebar-width.ts +37 -0
  656. package/apps/sessrumnir/src/shared/theme/builtin-ids.ts +16 -0
  657. package/apps/sessrumnir/src/shared/theme/resolve.test.ts +66 -0
  658. package/apps/sessrumnir/src/shared/theme/resolve.ts +29 -0
  659. package/apps/sessrumnir/src/shared/theme/syntax-defaults.ts +24 -0
  660. package/apps/sessrumnir/src/shared/theme/theme-file.test.ts +88 -0
  661. package/apps/sessrumnir/src/shared/theme/theme-file.ts +132 -0
  662. package/apps/sessrumnir/src/shared/theme/tokens.test.ts +35 -0
  663. package/apps/sessrumnir/src/shared/theme/tokens.ts +86 -0
  664. package/apps/sessrumnir/src/shared/tsconfig.json +17 -0
  665. package/apps/sessrumnir/src/shared/untrusted-data.test.ts +32 -0
  666. package/apps/sessrumnir/src/shared/untrusted-data.ts +22 -0
  667. package/apps/sessrumnir/src/shared/version-compare.test.ts +33 -0
  668. package/apps/sessrumnir/src/shared/version-compare.ts +50 -0
  669. package/apps/sessrumnir/src/shared/workflow-control.ts +33 -0
  670. package/apps/sessrumnir/src/web-server.ts +315 -0
  671. package/apps/sessrumnir/tsconfig.json +26 -0
  672. package/apps/smidja/smidja_build.py +45 -0
  673. package/apps/smidja/smidja_build_review.py +76 -0
  674. package/apps/smidja/smidja_build_test.py +79 -0
  675. package/apps/smidja/smidja_document.py +76 -0
  676. package/apps/smidja/smidja_modules/agent_cc.py +15 -0
  677. package/apps/smidja/smidja_modules/agent_opencode.py +194 -0
  678. package/apps/smidja/smidja_modules/agent_pi.py +392 -0
  679. package/apps/smidja/smidja_modules/agents.py +993 -0
  680. package/apps/smidja/smidja_modules/changes.py +103 -0
  681. package/apps/smidja/smidja_modules/console.py +131 -0
  682. package/apps/smidja/smidja_modules/data_types.py +447 -0
  683. package/apps/smidja/smidja_modules/gates.py +108 -0
  684. package/apps/smidja/smidja_modules/git_helper.py +120 -0
  685. package/apps/smidja/smidja_modules/lm_local.py +144 -0
  686. package/apps/smidja/smidja_modules/permissions.py +194 -0
  687. package/apps/smidja/smidja_modules/prompts.py +21 -0
  688. package/apps/smidja/smidja_modules/quality.py +240 -0
  689. package/apps/smidja/smidja_modules/runner.py +155 -0
  690. package/apps/smidja/smidja_modules/session.py +50 -0
  691. package/apps/smidja/smidja_modules/stall_watch.py +149 -0
  692. package/apps/smidja/smidja_modules/tracer.py +271 -0
  693. package/apps/smidja/smidja_modules/utils.py +77 -0
  694. package/apps/smidja/smidja_orchestrate.py +162 -0
  695. package/apps/smidja/smidja_plan.py +45 -0
  696. package/apps/smidja/smidja_plan_build.py +55 -0
  697. package/apps/smidja/smidja_plan_build_test.py +86 -0
  698. package/apps/smidja/smidja_plan_build_test_quality.py +98 -0
  699. package/apps/smidja/smidja_prompt.py +44 -0
  700. package/apps/smidja/smidja_quality.py +49 -0
  701. package/apps/smidja/smidja_recon_iv.py +202 -0
  702. package/apps/smidja/smidja_scout.py +45 -0
  703. package/apps/smidja/smidja_simple_sdlc.py +183 -0
  704. package/apps/smidja/smidja_smidja_config/smidja.config.yaml +143 -0
  705. package/apps/smidja-factory/AGENTS.md +165 -0
  706. package/apps/smidja-factory/README.md +3 -0
  707. package/apps/smidja-factory/SKILL.md +18 -0
  708. package/apps/smidja-factory/apps/visualizer/.oxlintrc.json +18 -0
  709. package/apps/smidja-factory/apps/visualizer/bun.lock +269 -0
  710. package/apps/smidja-factory/apps/visualizer/desktop/icon.png +0 -0
  711. package/apps/smidja-factory/apps/visualizer/desktop/main.js +140 -0
  712. package/apps/smidja-factory/apps/visualizer/desktop/package-lock.json +871 -0
  713. package/apps/smidja-factory/apps/visualizer/desktop/package.json +18 -0
  714. package/apps/smidja-factory/apps/visualizer/desktop/preload.js +19 -0
  715. package/apps/smidja-factory/apps/visualizer/index.html +28 -0
  716. package/apps/smidja-factory/apps/visualizer/package.json +31 -0
  717. package/apps/smidja-factory/apps/visualizer/public/icon.svg +7 -0
  718. package/apps/smidja-factory/apps/visualizer/public/models/claude.png +0 -0
  719. package/apps/smidja-factory/apps/visualizer/public/models/gemini.png +0 -0
  720. package/apps/smidja-factory/apps/visualizer/public/models/kimi.png +0 -0
  721. package/apps/smidja-factory/apps/visualizer/public/models/openai.png +0 -0
  722. package/apps/smidja-factory/apps/visualizer/public/models/zai.png +0 -0
  723. package/apps/smidja-factory/apps/visualizer/server/chat.ts +425 -0
  724. package/apps/smidja-factory/apps/visualizer/server/db.ts +1050 -0
  725. package/apps/smidja-factory/apps/visualizer/server/index.ts +648 -0
  726. package/apps/smidja-factory/apps/visualizer/server/model-catalog.ts +90 -0
  727. package/apps/smidja-factory/apps/visualizer/server/roster-api.ts +75 -0
  728. package/apps/smidja-factory/apps/visualizer/server/roster_resolve.py +170 -0
  729. package/apps/smidja-factory/apps/visualizer/server/settings.ts +106 -0
  730. package/apps/smidja-factory/apps/visualizer/shared/types.ts +630 -0
  731. package/apps/smidja-factory/apps/visualizer/src/App.vue +379 -0
  732. package/apps/smidja-factory/apps/visualizer/src/components/ChatInput.vue +155 -0
  733. package/apps/smidja-factory/apps/visualizer/src/components/ChatThread.vue +219 -0
  734. package/apps/smidja-factory/apps/visualizer/src/components/ChatView.vue +335 -0
  735. package/apps/smidja-factory/apps/visualizer/src/components/DecisionsView.vue +177 -0
  736. package/apps/smidja-factory/apps/visualizer/src/components/DetailSection.vue +76 -0
  737. package/apps/smidja-factory/apps/visualizer/src/components/MemoryView.vue +397 -0
  738. package/apps/smidja-factory/apps/visualizer/src/components/PhaseDetail.vue +1354 -0
  739. package/apps/smidja-factory/apps/visualizer/src/components/PhaseDots.vue +55 -0
  740. package/apps/smidja-factory/apps/visualizer/src/components/SessionCard.vue +583 -0
  741. package/apps/smidja-factory/apps/visualizer/src/components/SessionMiniCard.vue +58 -0
  742. package/apps/smidja-factory/apps/visualizer/src/components/SessionPanel.vue +272 -0
  743. package/apps/smidja-factory/apps/visualizer/src/components/SessionStartForm.vue +152 -0
  744. package/apps/smidja-factory/apps/visualizer/src/components/SessionTrace.vue +1157 -0
  745. package/apps/smidja-factory/apps/visualizer/src/components/SessionsList.vue +96 -0
  746. package/apps/smidja-factory/apps/visualizer/src/components/SettingsView.vue +152 -0
  747. package/apps/smidja-factory/apps/visualizer/src/components/StatChip.vue +98 -0
  748. package/apps/smidja-factory/apps/visualizer/src/components/StatsView.vue +382 -0
  749. package/apps/smidja-factory/apps/visualizer/src/components/StatusChip.vue +73 -0
  750. package/apps/smidja-factory/apps/visualizer/src/components/ToolCallCard.vue +156 -0
  751. package/apps/smidja-factory/apps/visualizer/src/lib/api.ts +177 -0
  752. package/apps/smidja-factory/apps/visualizer/src/lib/chat-api.ts +95 -0
  753. package/apps/smidja-factory/apps/visualizer/src/lib/chat-store.ts +351 -0
  754. package/apps/smidja-factory/apps/visualizer/src/lib/events.ts +131 -0
  755. package/apps/smidja-factory/apps/visualizer/src/lib/format.ts +92 -0
  756. package/apps/smidja-factory/apps/visualizer/src/lib/highlight.ts +54 -0
  757. package/apps/smidja-factory/apps/visualizer/src/lib/markdown.ts +120 -0
  758. package/apps/smidja-factory/apps/visualizer/src/lib/models.ts +42 -0
  759. package/apps/smidja-factory/apps/visualizer/src/lib/router.ts +67 -0
  760. package/apps/smidja-factory/apps/visualizer/src/lib/types.ts +61 -0
  761. package/apps/smidja-factory/apps/visualizer/src/main.ts +13 -0
  762. package/apps/smidja-factory/apps/visualizer/src/style.css +592 -0
  763. package/apps/smidja-factory/apps/visualizer/tsconfig.json +30 -0
  764. package/apps/smidja-factory/apps/visualizer/vite.config.js +32 -0
  765. package/apps/smidja-factory/apps/visualizer/vite.config.ts +31 -0
  766. package/apps/smidja-factory/cookbooks/create_config.md +61 -0
  767. package/apps/smidja-factory/cookbooks/create_smidja.md +120 -0
  768. package/apps/smidja-factory/cookbooks/how_to_prompt_for_the_eng.md +110 -0
  769. package/apps/smidja-factory/cookbooks/install.md +56 -0
  770. package/apps/smidja-factory/cookbooks/run_smidja.md +122 -0
  771. package/apps/smidja-factory/cookbooks/smidja_overview.md +88 -0
  772. package/apps/smidja-factory/cookbooks/update_config.md +103 -0
  773. package/apps/smidja-factory/cookbooks/update_modules.md +100 -0
  774. package/apps/smidja-factory/cookbooks/update_smidja.md +90 -0
  775. package/apps/smidja-factory/docs/enhancement-plan.md +420 -0
  776. package/apps/smidja-factory/references/config.md +202 -0
  777. package/apps/smidja-factory/references/handoff.md +161 -0
  778. package/apps/smidja-factory/references/observability.md +166 -0
  779. package/apps/smidja-factory/scripts/install.py +113 -0
  780. package/apps/smidja-factory/scripts/make_config.py +35 -0
  781. package/apps/smidja-factory/scripts/make_smidja.py +119 -0
  782. package/apps/smidja-factory/skills/add-or-edit-ai-models/SKILL.md +151 -0
  783. package/apps/smidja-factory/skills/create-new-agent/SKILL.md +125 -0
  784. package/apps/smidja-factory/skills/create-new-teams/SKILL.md +151 -0
  785. package/apps/smidja-factory/skills/git-ops/README.md +25 -0
  786. package/apps/smidja-factory/skills/git-ops/SKILL.md +18 -0
  787. package/apps/smidja-factory/skills/git-ops/create_branch.sh +79 -0
  788. package/apps/smidja-factory/skills/git-ops/safe_commit.sh +82 -0
  789. package/apps/smidja-factory/skills/git-ops/sync_upstream.sh +94 -0
  790. package/apps/smidja-factory/skills/how-to-run-agent-teams/SKILL.md +117 -0
  791. package/apps/smidja-factory/skills/smidja-instructions/SKILL.md +168 -0
  792. package/apps/smidja-factory/skills/smidja-launcher/SKILL.md +155 -0
  793. package/apps/smidja-factory/skills/smidja-start/SKILL.md +266 -0
  794. package/apps/smidja-factory/skills/volundr/SKILL.md +232 -0
  795. package/apps/smidja-factory/skills/volundr/prompt/dispatch-prompt.md +91 -0
  796. package/apps/smidja-factory/skills/volundr/prompt/volundr-system.md +63 -0
  797. package/apps/smidja-factory/skills/volundr/scripts/volundr-observe.py +52 -0
  798. package/apps/smidja-factory/skills/volundr/scripts/volundr-recall.py +54 -0
  799. package/apps/smidja-factory/skills/volundr/scripts/volundr-status.py +51 -0
  800. package/apps/smidja-factory/skills/volundr/scripts/volundr-teach.py +47 -0
  801. package/apps/smidja-factory/templates/env.sample +28 -0
  802. package/apps/smidja-factory/templates/harness_engineering/subagents.ts +706 -0
  803. package/apps/smidja-factory/templates/harness_engineering/themeMap.ts +145 -0
  804. package/apps/smidja-factory/templates/justfile +89 -0
  805. package/apps/smidja-factory/templates/prompt_engineering/builder/system.md +13 -0
  806. package/apps/smidja-factory/templates/prompt_engineering/builder/user.md +34 -0
  807. package/apps/smidja-factory/templates/prompt_engineering/documenter/system.md +17 -0
  808. package/apps/smidja-factory/templates/prompt_engineering/documenter/user.md +48 -0
  809. package/apps/smidja-factory/templates/prompt_engineering/orchestrator/system.md +55 -0
  810. package/apps/smidja-factory/templates/prompt_engineering/orchestrator/user.md +39 -0
  811. package/apps/smidja-factory/templates/prompt_engineering/planner/system.md +21 -0
  812. package/apps/smidja-factory/templates/prompt_engineering/planner/user.md +45 -0
  813. package/apps/smidja-factory/templates/prompt_engineering/recon_orchestrator/system.md +51 -0
  814. package/apps/smidja-factory/templates/prompt_engineering/recon_orchestrator/user.md +26 -0
  815. package/apps/smidja-factory/templates/prompt_engineering/reviewer/system.md +16 -0
  816. package/apps/smidja-factory/templates/prompt_engineering/reviewer/user.md +44 -0
  817. package/apps/smidja-factory/templates/prompt_engineering/scout/system.md +32 -0
  818. package/apps/smidja-factory/templates/prompt_engineering/scout/user.md +34 -0
  819. package/apps/smidja-factory/templates/smidja/smidja_build.py +45 -0
  820. package/apps/smidja-factory/templates/smidja/smidja_build_review.py +76 -0
  821. package/apps/smidja-factory/templates/smidja/smidja_build_test.py +79 -0
  822. package/apps/smidja-factory/templates/smidja/smidja_document.py +76 -0
  823. package/apps/smidja-factory/templates/smidja/smidja_modules/agent_cc.py +15 -0
  824. package/apps/smidja-factory/templates/smidja/smidja_modules/agent_opencode.py +194 -0
  825. package/apps/smidja-factory/templates/smidja/smidja_modules/agent_pi.py +392 -0
  826. package/apps/smidja-factory/templates/smidja/smidja_modules/agents.py +993 -0
  827. package/apps/smidja-factory/templates/smidja/smidja_modules/changes.py +103 -0
  828. package/apps/smidja-factory/templates/smidja/smidja_modules/console.py +131 -0
  829. package/apps/smidja-factory/templates/smidja/smidja_modules/data_types.py +447 -0
  830. package/apps/smidja-factory/templates/smidja/smidja_modules/gates.py +108 -0
  831. package/apps/smidja-factory/templates/smidja/smidja_modules/git_helper.py +120 -0
  832. package/apps/smidja-factory/templates/smidja/smidja_modules/lm_local.py +144 -0
  833. package/apps/smidja-factory/templates/smidja/smidja_modules/permissions.py +194 -0
  834. package/apps/smidja-factory/templates/smidja/smidja_modules/prompts.py +21 -0
  835. package/apps/smidja-factory/templates/smidja/smidja_modules/quality.py +240 -0
  836. package/apps/smidja-factory/templates/smidja/smidja_modules/runner.py +155 -0
  837. package/apps/smidja-factory/templates/smidja/smidja_modules/session.py +50 -0
  838. package/apps/smidja-factory/templates/smidja/smidja_modules/stall_watch.py +149 -0
  839. package/apps/smidja-factory/templates/smidja/smidja_modules/tracer.py +271 -0
  840. package/apps/smidja-factory/templates/smidja/smidja_modules/utils.py +77 -0
  841. package/apps/smidja-factory/templates/smidja/smidja_orchestrate.py +162 -0
  842. package/apps/smidja-factory/templates/smidja/smidja_plan.py +45 -0
  843. package/apps/smidja-factory/templates/smidja/smidja_plan_build.py +55 -0
  844. package/apps/smidja-factory/templates/smidja/smidja_plan_build_test.py +86 -0
  845. package/apps/smidja-factory/templates/smidja/smidja_plan_build_test_quality.py +98 -0
  846. package/apps/smidja-factory/templates/smidja/smidja_prompt.py +44 -0
  847. package/apps/smidja-factory/templates/smidja/smidja_quality.py +49 -0
  848. package/apps/smidja-factory/templates/smidja/smidja_recon_iv.py +202 -0
  849. package/apps/smidja-factory/templates/smidja/smidja_scout.py +45 -0
  850. package/apps/smidja-factory/templates/smidja/smidja_simple_sdlc.py +183 -0
  851. package/apps/smidja-factory/templates/smidja.config.yaml +143 -0
  852. package/bin/app-build.sh +36 -0
  853. package/bin/electron-lib.sh +25 -0
  854. package/bin/sessrumnir.sh +1 -1
  855. package/bin/ymir-install.sh +7 -0
  856. package/install.sh +88 -0
  857. package/package.json +16 -7
  858. package/scripts/electron.sh +6 -2
  859. package/.agents/bus/messages.json +0 -1
  860. package/.agents/config/agents.machine.example.yaml +0 -16
  861. package/.pi/mcp.json +0 -16
  862. package/assets/reference/state/.branch-eligible-owner +0 -4
  863. package/assets/reference/state/.branch-mirror-cursor +0 -1
  864. package/assets/reference/state/.branch-outcomes-cursor +0 -1
  865. package/assets/reference/state/.branch-session +0 -1
  866. package/assets/reference/state/.count-default_wF_p5 +0 -1
  867. package/assets/reference/state/.count-default_wF_p6 +0 -1
  868. package/assets/reference/state/.count-default_wR_p2 +0 -1
  869. package/assets/reference/state/.count-default_wS_p2 +0 -1
  870. package/assets/reference/state/.count-firstmate_fm-server-knowledge +0 -1
  871. package/assets/reference/state/.hash-default_wF_p5 +0 -1
  872. package/assets/reference/state/.hash-default_wF_p6 +0 -1
  873. package/assets/reference/state/.hash-default_wR_p2 +0 -1
  874. package/assets/reference/state/.hash-default_wS_p2 +0 -1
  875. package/assets/reference/state/.hash-firstmate_fm-server-knowledge +0 -1
  876. package/assets/reference/state/.heartbeat-streak +0 -1
  877. package/assets/reference/state/.inactive-outcome-reconcile +0 -2
  878. package/assets/reference/state/.lock +0 -1
  879. package/assets/reference/state/.pi-branch-extension-loaded +0 -1
  880. package/assets/reference/state/.pi-turnend-extension-loaded +0 -2
  881. package/assets/reference/state/.pi-watch-extension-loaded +0 -2
  882. package/assets/reference/state/.seen-server-knowledge_turn-ended +0 -1
  883. package/assets/reference/state/.session-start-agents-baseline +0 -2
  884. package/assets/reference/state/.session-start-complete +0 -1
  885. package/assets/reference/state/.stale-default_wF_p5 +0 -1
  886. package/assets/reference/state/.stale-default_wR_p2 +0 -1
  887. package/assets/reference/state/.stale-firstmate_fm-server-knowledge +0 -1
  888. package/assets/reference/state/.stale-since-default_wF_p5 +0 -1
  889. package/assets/reference/state/.stale-since-default_wR_p2 +0 -1
  890. package/assets/reference/state/.stale-since-firstmate_fm-server-knowledge +0 -1
  891. package/assets/reference/state/.startup-network.delivered +0 -1
  892. package/assets/reference/state/.startup-network.report +0 -1
  893. package/assets/reference/state/.startup-network.status +0 -10
  894. package/assets/reference/state/.startup-network.timings +0 -6
  895. package/assets/reference/state/.trace-context-effective +0 -1
  896. package/assets/reference/state/.wake-queue +0 -0
  897. package/assets/reference/state/.wake-queue.seq +0 -1
  898. package/assets/reference/state/.wake-rows.consume.BcqXSq +0 -0
  899. package/assets/reference/state/.wake-rows.consume.NDZZei +0 -0
  900. package/assets/reference/state/.wake-rows.consume.VGkhYm +0 -0
  901. package/assets/reference/state/.wake-rows.consume.tR2rTy +0 -0
  902. package/assets/reference/state/.watch-arm-output.3581p6 +0 -0
  903. package/assets/reference/state/.watch-arm-output.3ciDNP +0 -0
  904. package/assets/reference/state/.watch-arm-output.SksR6k +0 -0
  905. package/assets/reference/state/.watch-cycle-exits.log +0 -22
  906. package/assets/reference/state/.watch-deliveries.log +0 -19
  907. package/assets/reference/state/.watch-triage.log +0 -38
  908. package/assets/reference/state/.watch.lock/fm-home +0 -1
  909. package/assets/reference/state/.watch.lock/pid +0 -1
  910. package/assets/reference/state/.watch.lock/pid-identity +0 -1
  911. package/assets/reference/state/.watch.lock/watcher-path +0 -1
  912. package/assets/reference/state/.watch.lock.owner.qqbxmS/fm-home +0 -1
  913. package/assets/reference/state/.watch.lock.owner.qqbxmS/pid +0 -1
  914. package/assets/reference/state/.watch.lock.owner.qqbxmS/pid-identity +0 -1
  915. package/assets/reference/state/.watch.lock.owner.qqbxmS/watcher-path +0 -1
  916. package/assets/reference/state/.watcher-down +0 -1
  917. package/assets/reference/state/branch-outcomes.jsonl +0 -1
  918. package/assets/reference/state/branch-session/2026-09-04T16-49-38-671Z_01a06d53-6b6f-7619-930d-e6dfd9a4a29e.jsonl +0 -211
  919. package/assets/reference/state/home-summary.json +0 -42
  920. package/assets/reference/state/server-knowledge.busy-gen +0 -1
  921. package/assets/reference/state/server-knowledge.busy-state +0 -1
  922. package/assets/reference/state/server-knowledge.herdr-presentation +0 -12
  923. package/assets/reference/state/server-knowledge.meta +0 -18
  924. package/assets/reference/state/server-knowledge.pi-ext.ts +0 -26
  925. package/assets/reference/state/server-knowledge.turn-ended +0 -0
  926. /package/{assets/reference/state/.last-check → apps/hlidskjalf/src/app/.gitkeep} +0 -0
  927. /package/{assets/reference/state/.last-heartbeat → apps/hlidskjalf/src/components/.gitkeep} +0 -0
  928. /package/{assets/reference/state/.last-watcher-beat → apps/hlidskjalf/src/services/.gitkeep} +0 -0
  929. /package/{assets/reference/state/.main-eligible-rows.tmp.p2HMKo → apps/smidja/smidja_modules/__init__.py} +0 -0
  930. /package/{assets/reference/state/.main-eligible-rows.tmp.Exxm82 → apps/smidja-factory/templates/smidja/smidja_modules/__init__.py} +0 -0
@@ -0,0 +1,1045 @@
1
+ import { useTranslation } from 'react-i18next'
2
+ import { useAppStore, countPromptsWaitingElsewhere, formatPromptsWaiting } from '../store'
3
+ import { summarizeBackgroundActivity, workspaceActivityIndicator } from './sidebar-activity'
4
+ import { pathGroupKey, pathsEqual } from '../../../shared/path-compare'
5
+ import { SESSRUMNIR_PRODUCT_NAME } from '../../../shared/product-name'
6
+ import { clsx } from 'clsx'
7
+ import {
8
+ Home,
9
+ MessageSquare,
10
+ Settings,
11
+ FolderOpen,
12
+ Plus,
13
+ PanelLeftClose,
14
+ Clock,
15
+ Activity,
16
+ LayoutDashboard,
17
+ Package,
18
+ Layers,
19
+ ChevronDown,
20
+ Check,
21
+ Trash2,
22
+ StickyNote,
23
+ Archive,
24
+ Sparkles,
25
+ Stethoscope,
26
+ Pencil,
27
+ Workflow as WorkflowIcon,
28
+ } from 'lucide-react'
29
+ import { useMemo, useState, useRef } from 'react'
30
+ import { StatusPopover } from './status-popover'
31
+ import { useContextMenu, buildSessionContextMenu } from './context-menu'
32
+ import { getSessionEngineLabel, getSessionRowLabels, hasMixedSessionEngines } from './sidebar-session-labels'
33
+ import { ResizeHandle } from './resize-handle'
34
+ import { findSessionPreview, getSessionTitle } from '../utils/session-title'
35
+ import { formatRelativeTime } from '../utils/format-relative-time'
36
+ import { SessionRuntimeIndicator } from './session-runtime-indicator'
37
+ import { resolveRunSessionId } from '../utils/workflow-runs'
38
+ import { useGlobalWorkflowOpen } from '../hooks'
39
+ import { clampSidebarWidth, resolveSidebarWidth } from '../../../shared/sidebar-width'
40
+ import type { SessionListItem } from '../../../shared/ipc-contracts'
41
+
42
+ /** Views reachable from the sidebar's Tools group. */
43
+ type ToolView = 'packages' | 'notes' | 'skills' | 'diagnostics' | 'settings'
44
+
45
+ /** Cap how many workspace groups appear in the Recent list. */
46
+ const MAX_RECENT_GROUPS = 12
47
+ /** Cap sessions shown inside an expanded workspace group. */
48
+ const MAX_SESSIONS_PER_GROUP = 12
49
+
50
+ interface RecentSessionGroup {
51
+ projectPath: string
52
+ projectName: string
53
+ sessions: SessionListItem[]
54
+ latest: SessionListItem
55
+ }
56
+
57
+ export function Sidebar(): React.JSX.Element {
58
+ const { t } = useTranslation()
59
+ const currentView = useAppStore((state) => state.currentView)
60
+ const setCurrentView = useAppStore((state) => state.setCurrentView)
61
+ const toggleSidebar = useAppStore((state) => state.toggleSidebar)
62
+ const sessionState = useAppStore((state) => state.sessionState)
63
+ const sessionList = useAppStore((state) => state.sessionList)
64
+ const sessionRuntimes = useAppStore((state) => state.sessionRuntimes)
65
+ const activeSessionRuntimeId = useAppStore((state) => state.activeSessionRuntimeId)
66
+ const createNewSession = useAppStore((state) => state.createNewSession)
67
+ const setTaskLauncherOpen = useAppStore((state) => state.setTaskLauncherOpen)
68
+ const openFolderAsWorkspace = useAppStore((state) => state.openFolderAsWorkspace)
69
+ const openWorkflowRunsForSession = useAppStore((state) => state.openWorkflowRunsForSession)
70
+ const openWorkflowRunsForWorkspace = useAppStore((state) => state.openWorkflowRunsForWorkspace)
71
+ const setWorkflowPanelOpen = useAppStore((state) => state.setWorkflowPanelOpen)
72
+ const workflowPanelOpen = useAppStore((state) => state.workflowPanelOpen)
73
+ const workflowPanelWorkspaceId = useAppStore((state) => state.workflowPanelWorkspaceId)
74
+ const workflowPanelFilter = useAppStore((state) => state.workflowPanelFilter)
75
+ const globalWorkflowOpen = useGlobalWorkflowOpen()
76
+ const setSessionsScope = useAppStore((state) => state.setSessionsScope)
77
+ const activeWorkspace = useAppStore((state) => state.activeWorkspace)
78
+ const archivedSessions = useAppStore((state) => state.archivedSessions)
79
+ const archiveSession = useAppStore((state) => state.archiveSession)
80
+ const unarchiveSession = useAppStore((state) => state.unarchiveSession)
81
+ const deleteSession = useAppStore((state) => state.deleteSession)
82
+ const setSessionName = useAppStore((state) => state.setSessionName)
83
+ const persistedWidth = useAppStore((state) => state.settings?.sidebarWidth)
84
+ const saveSidebarWidth = useAppStore((state) => state.saveSidebarWidth)
85
+
86
+ const { show: showMenu, ContextMenuComponent: SessionMenu } = useContextMenu()
87
+
88
+ const [archivedOpen, setArchivedOpen] = useState(false)
89
+
90
+ // The live width during a drag. Kept local so dragging never writes
91
+ // settings.json; the draft outlives the drag so the row does not jump while the
92
+ // save round-trips, and a remount falls back to the saved value.
93
+ const [widthDraft, setWidthDraft] = useState<number | null>(null)
94
+ const sidebarWidth = resolveSidebarWidth(widthDraft, persistedWidth)
95
+ const savedWidth = resolveSidebarWidth(null, persistedWidth)
96
+ // The handle registers its mousemove listener once per drag, so its callback
97
+ // closes over a single render. Deltas must therefore accumulate through the
98
+ // state updater — reading the width off a render-scoped value (or a ref written
99
+ // during render) drops every event that lands before React re-renders.
100
+ const widthRef = useRef(sidebarWidth)
101
+
102
+ const applyResizeDelta = (delta: number): void => {
103
+ setWidthDraft((current) => {
104
+ const next = clampSidebarWidth((current ?? savedWidth) + delta)
105
+ // Mirrored for onResizeEnd, which has no access to the updated state.
106
+ widthRef.current = next
107
+ return next
108
+ })
109
+ }
110
+
111
+ // Inline session rename. Only the active session can be renamed (Pi's rename
112
+ // targets it), and it's reachable from two spots — the Current Session panel
113
+ // (`'current'`) and its highlighted row in Recent Sessions (`'recent'`).
114
+ const [renamingWhere, setRenamingWhere] = useState<'current' | 'recent' | null>(null)
115
+ const [renameValue, setRenameValue] = useState('')
116
+ const renameCancelRef = useRef(false)
117
+
118
+ const startSessionRename = (where: 'current' | 'recent'): void => {
119
+ renameCancelRef.current = false
120
+ // Prefill with the explicit name only; a timestamp/guid is not a name.
121
+ setRenameValue(sessionState?.sessionName ?? '')
122
+ setRenamingWhere(where)
123
+ }
124
+
125
+ // Single commit path (both Enter and Escape blur the input, which lands here).
126
+ const finishSessionRename = (): void => {
127
+ const cancelled = renameCancelRef.current
128
+ renameCancelRef.current = false
129
+ setRenamingWhere(null)
130
+ // Pi's set_session_name RPC rejects an empty name ("cannot be empty"), so an
131
+ // empty commit is a no-op (keeps the current name) rather than a doomed call.
132
+ const trimmed = renameValue.trim()
133
+ if (!cancelled && trimmed) setSessionName(trimmed)
134
+ }
135
+
136
+ const renderRenameInput = (): React.JSX.Element => (
137
+ <input
138
+ type="text"
139
+ value={renameValue}
140
+ onChange={(e) => setRenameValue(e.target.value)}
141
+ onFocus={(e) => e.target.select()}
142
+ onClick={(e) => e.stopPropagation()}
143
+ onKeyDown={(e) => {
144
+ if (e.key === 'Enter') {
145
+ e.preventDefault()
146
+ e.currentTarget.blur()
147
+ } else if (e.key === 'Escape') {
148
+ e.preventDefault()
149
+ renameCancelRef.current = true
150
+ e.currentTarget.blur()
151
+ }
152
+ }}
153
+ onBlur={finishSessionRename}
154
+ placeholder={t('sidebar.sessionNamePlaceholder')}
155
+ autoFocus
156
+ className="min-w-0 flex-1 rounded border border-border-strong bg-card px-2 py-0.5 text-sm text-primary placeholder:text-faint focus:border-focus focus:outline-none"
157
+ />
158
+ )
159
+
160
+ // Archived sessions live in their own collapsible section; Recent excludes them.
161
+ const activeSessions = useMemo(
162
+ () => sessionList.filter((s) => !(s.sessionId in archivedSessions)),
163
+ [sessionList, archivedSessions]
164
+ )
165
+ const archivedList = useMemo(
166
+ () => sessionList.filter((s) => s.sessionId in archivedSessions),
167
+ [sessionList, archivedSessions]
168
+ )
169
+
170
+ // Group recents by project folder (path). Display name = folder basename.
171
+ const recentGroups = useMemo((): RecentSessionGroup[] => {
172
+ // Group key is case-fold only on win32 (shared path-compare helper).
173
+ const byProject = new Map<string, { displayPath: string; sessions: SessionListItem[] }>()
174
+ for (const session of activeSessions) {
175
+ const displayPath = session.projectPath || 'unknown'
176
+ const key = pathGroupKey(displayPath)
177
+ const existing = byProject.get(key)
178
+ if (existing) existing.sessions.push(session)
179
+ else byProject.set(key, { displayPath, sessions: [session] })
180
+ }
181
+
182
+ const groups: RecentSessionGroup[] = []
183
+ for (const { displayPath, sessions } of byProject.values()) {
184
+ const sorted = [...sessions].sort((a, b) => b.lastModified - a.lastModified)
185
+ const latest = sorted[0]
186
+ if (!latest) continue
187
+ const folderName =
188
+ latest.projectName?.trim() ||
189
+ displayPath.split(/[\\/]/).filter(Boolean).pop() ||
190
+ displayPath
191
+ groups.push({
192
+ projectPath: displayPath,
193
+ projectName: folderName,
194
+ sessions: sorted.slice(0, MAX_SESSIONS_PER_GROUP),
195
+ latest,
196
+ })
197
+ }
198
+
199
+ groups.sort((a, b) => b.latest.lastModified - a.latest.lastModified)
200
+ return groups.slice(0, MAX_RECENT_GROUPS)
201
+ }, [activeSessions])
202
+
203
+ // Explicit expand/collapse overrides. Folders default to collapsed except the
204
+ // one that contains the active session (until the user toggles).
205
+ const [expandOverride, setExpandOverride] = useState<Record<string, boolean>>({})
206
+
207
+ const activeProjectKey = useMemo(() => {
208
+ if (!sessionState?.sessionFile) return null
209
+ const active = activeSessions.find((s) => s.path === sessionState.sessionFile)
210
+ return active ? pathGroupKey(active.projectPath || 'unknown') : null
211
+ }, [activeSessions, sessionState?.sessionFile])
212
+
213
+ const isGroupExpanded = (projectPath: string): boolean => {
214
+ const key = pathGroupKey(projectPath)
215
+ if (Object.prototype.hasOwnProperty.call(expandOverride, key)) {
216
+ return expandOverride[key]
217
+ }
218
+ return activeProjectKey === key
219
+ }
220
+
221
+ const toggleGroup = (projectPath: string): void => {
222
+ const key = pathGroupKey(projectPath)
223
+ setExpandOverride((prev) => ({
224
+ ...prev,
225
+ [key]: !isGroupExpanded(projectPath),
226
+ }))
227
+ }
228
+
229
+ // The live session state has no preview, so the Current Session panel would
230
+ // fall back to the raw id while the same session's Recent row shows its first
231
+ // message. Both read the same preview instead.
232
+ const currentSessionPreview = useMemo(
233
+ () => findSessionPreview(sessionList, sessionState?.sessionFile),
234
+ [sessionList, sessionState?.sessionFile]
235
+ )
236
+
237
+ // Gated on every known session, not on one section's slice, so the same chat
238
+ // carries the same tag in Recent, in a folder group and under Archived.
239
+ const showEngineTags = useMemo(() => hasMixedSessionEngines(sessionList), [sessionList])
240
+
241
+ const recentSessionsForWorkspace = useMemo(() => {
242
+ if (!activeWorkspace?.path) return []
243
+ return activeSessions
244
+ .filter((session) => pathsEqual(session.projectPath, activeWorkspace.path))
245
+ .sort((a, b) => b.lastModified - a.lastModified)
246
+ .slice(0, MAX_SESSIONS_PER_GROUP)
247
+ }, [activeSessions, activeWorkspace?.path])
248
+
249
+ const startNewSession = async (): Promise<void> => {
250
+ if (!activeWorkspace) {
251
+ setCurrentView('home')
252
+ return
253
+ }
254
+ setCurrentView('chat')
255
+ await createNewSession()
256
+ }
257
+
258
+ const openProject = async (): Promise<void> => {
259
+ const path = await window.piDesktop.system.openDialog({ title: t('dialogs.openProject.title') })
260
+ if (path) await openFolderAsWorkspace(path)
261
+ }
262
+
263
+ // A tool view is only "showing" when nothing covers it — the global workflow
264
+ // surface replaces the main pane while currentView stays put behind it.
265
+ const toolViewShowing = (view: ToolView): boolean => currentView === view && !globalWorkflowOpen
266
+
267
+ // Tool entries are toggles, like every other tab-like control here: clicking
268
+ // the one already on screen returns to Chat (what the Tools tab's close
269
+ // button does), while a covered one is revealed rather than dismissed.
270
+ const openToolView = (view: ToolView): void => {
271
+ const showing = toolViewShowing(view)
272
+ setWorkflowPanelOpen(false)
273
+ setCurrentView(showing ? 'chat' : view)
274
+ }
275
+
276
+ // Workspace auto-switch/create + session switch + show Chat, shared with the
277
+ // session panel and the quick switcher.
278
+ const openSession = useAppStore((state) => state.openSessionItem)
279
+
280
+ const handleSessionRightClick = (e: React.MouseEvent, session: SessionListItem): void => {
281
+ // Prevent the app-level document-level contextmenu handler from also
282
+ // firing (which would build & show a *default* menu on top of ours).
283
+ // React's synthetic stopPropagation isn't enough — that handler is
284
+ // attached to `document` and fires on native bubbling.
285
+ e.nativeEvent.stopPropagation()
286
+ const isActive = sessionState?.sessionFile === session.path
287
+ showMenu(
288
+ e,
289
+ buildSessionContextMenu(session, session.sessionId in archivedSessions, {
290
+ onOpen: (s) => { openSession(s) },
291
+ onArchive: (id) => archiveSession(id),
292
+ onUnarchive: (id) => unarchiveSession(id),
293
+ onDelete: (s) => { deleteSession(s) },
294
+ // Rename only offered for the active session (Pi renames the active one).
295
+ onRename: isActive ? () => startSessionRename('recent') : undefined,
296
+ onRuns: (s) => openWorkflowRunsForSession(resolveRunSessionId(s.piSessionId, s.sessionId) ?? s.sessionId),
297
+ })
298
+ )
299
+ }
300
+
301
+ // Right-click menu for the Current Session panel — same active session, so
302
+ // just the rename affordance.
303
+ const handleCurrentSessionRightClick = (e: React.MouseEvent): void => {
304
+ e.nativeEvent.stopPropagation()
305
+ showMenu(e, [
306
+ {
307
+ id: 'current-session-rename',
308
+ label: t('contextMenu.rename'),
309
+ icon: <Pencil size={14} />,
310
+ action: () => startSessionRename('current'),
311
+ },
312
+ ])
313
+ }
314
+
315
+ const renderSessionRow = (
316
+ session: SessionListItem,
317
+ options?: { nested?: boolean }
318
+ ): React.JSX.Element => {
319
+ const labels = getSessionRowLabels(session)
320
+ // Runs are keyed by Pi's header UUID, never the filename stem (the
321
+ // tags/archive registry key). The stem suffix IS the UUID, so it is a
322
+ // safe fallback when a row's header is unreadable.
323
+ const workflowSessionId = resolveRunSessionId(session.piSessionId, session.sessionId) ?? session.sessionId
324
+ const runtime = Object.values(sessionRuntimes).find((item) => item.sessionPath && pathsEqual(item.sessionPath, session.path))
325
+ const isActive = sessionState?.sessionFile === session.path || runtime?.runtimeId === activeSessionRuntimeId
326
+ const nested = options?.nested ?? false
327
+ const engineLabel = showEngineTags ? getSessionEngineLabel(session) : null
328
+
329
+ // Inline rename for the active row.
330
+ if (isActive && renamingWhere === 'recent') {
331
+ return (
332
+ <div
333
+ key={session.path}
334
+ className={clsx(
335
+ 'flex w-full items-center gap-2 rounded bg-card px-2 py-1.5',
336
+ nested && 'pl-2'
337
+ )}
338
+ >
339
+ <Clock size={12} className="shrink-0 text-muted" />
340
+ {renderRenameInput()}
341
+ </div>
342
+ )
343
+ }
344
+
345
+ return (
346
+ <div key={session.path} className="group relative">
347
+ <button
348
+ onClick={() => openSession(session)}
349
+ onDoubleClick={() => { if (isActive) startSessionRename('recent') }}
350
+ onContextMenu={(e) => handleSessionRightClick(e, session)}
351
+ // The full title leads, so a preview too long for the current width is
352
+ // still readable on hover.
353
+ title={
354
+ isActive
355
+ ? t('sidebar.sessionRow.tooltipActive', { title: labels.title })
356
+ : t('sidebar.sessionRow.tooltipInactive', { title: labels.title })
357
+ }
358
+ className={clsx(
359
+ 'flex w-full items-center gap-2 rounded px-2 py-1.5 pr-7 text-left text-sm transition-colors',
360
+ nested && 'pl-2',
361
+ isActive
362
+ ? 'bg-card text-primary'
363
+ : 'hover:bg-highlight text-muted hover:text-secondary'
364
+ )}
365
+ >
366
+ <Clock size={12} className="shrink-0" />
367
+ <div className="min-w-0 flex-1">
368
+ <div className="truncate">{labels.title}</div>
369
+ {/* The title is now the session's name or first message, so the time it
370
+ displaced moves here. Recent rows are already grouped by workspace,
371
+ which makes the project name the less useful of the two subtitles —
372
+ the home screen, which is not grouped, shows the project instead.
373
+ The engine leads the line only when both engines are present. */}
374
+ <div className="truncate text-[11px] text-faint">
375
+ {engineLabel && `${engineLabel} · `}
376
+ {formatRelativeTime(session.lastModified, Date.now())}
377
+ </div>
378
+ </div>
379
+ {runtime && <SessionRuntimeIndicator runtime={runtime} />}
380
+ </button>
381
+ {/* Sibling (not child) of the row button, so no nested interactive
382
+ elements: the row's click/double-click/context-menu never fire for
383
+ this icon. Shows an empty filtered state when the session has no runs. */}
384
+ <button
385
+ type="button"
386
+ onClick={() => openWorkflowRunsForSession(workflowSessionId)}
387
+ className="absolute right-1 top-1/2 -translate-y-1/2 rounded p-1 text-faint opacity-0 transition-opacity hover:bg-highlight hover:text-accent-fg focus-visible:opacity-100 group-hover:opacity-100"
388
+ title={t('sidebar.workflowRunsForSession')}
389
+ aria-label={t('sidebar.workflowRunsForSession')}
390
+ >
391
+ <WorkflowIcon size={12} />
392
+ </button>
393
+ </div>
394
+ )
395
+ }
396
+
397
+ const renderRecentGroup = (group: RecentSessionGroup): React.JSX.Element => {
398
+ const expanded = isGroupExpanded(group.projectPath)
399
+ const isCurrentFolder =
400
+ !!activeWorkspace?.path && pathsEqual(activeWorkspace.path, group.projectPath)
401
+ const count = group.sessions.length
402
+
403
+ return (
404
+ <div key={pathGroupKey(group.projectPath)} className="mb-1">
405
+ {/* Folder header — primary grouping unit */}
406
+ <button
407
+ type="button"
408
+ onClick={() => toggleGroup(group.projectPath)}
409
+ title={group.projectPath}
410
+ aria-expanded={expanded}
411
+ className={clsx(
412
+ 'flex w-full items-center gap-1.5 rounded px-2 py-1.5 text-left transition-colors',
413
+ isCurrentFolder
414
+ ? 'bg-accent-bg/40 text-primary'
415
+ : 'text-secondary hover:bg-highlight hover:text-primary'
416
+ )}
417
+ >
418
+ <ChevronDown
419
+ size={12}
420
+ className={clsx(
421
+ 'shrink-0 text-dim transition-transform',
422
+ !expanded && '-rotate-90'
423
+ )}
424
+ />
425
+ <FolderOpen
426
+ size={12}
427
+ className={clsx('shrink-0', isCurrentFolder ? 'text-accent-fg' : 'text-dim')}
428
+ />
429
+ <span className="min-w-0 flex-1 truncate text-xs font-medium">
430
+ {group.projectName}
431
+ </span>
432
+ <span className="shrink-0 text-[10px] text-faint">
433
+ {count}
434
+ </span>
435
+ </button>
436
+
437
+ {expanded && (
438
+ <div className="mt-0.5 space-y-0.5 border-l border-border/70 ml-3 pl-1">
439
+ {group.sessions.map((session) =>
440
+ renderSessionRow(session, { nested: true })
441
+ )}
442
+ </div>
443
+ )}
444
+ </div>
445
+ )
446
+ }
447
+
448
+ return (
449
+ <>
450
+ <aside
451
+ className="flex shrink-0 flex-col border-r border-border bg-app"
452
+ style={{ width: sidebarWidth }}
453
+ >
454
+ {/* Header */}
455
+ <div className="flex h-12 items-center justify-between border-b border-border px-3">
456
+ <div className="flex items-center gap-2">
457
+ <StatusPopover />
458
+ {/* Compact Home replaces the duplicate Pi-activity popover: workspace
459
+ activity already lives in the switcher row, tab icons, and switcher
460
+ dropdown, so the header keeps only system status + Home. */}
461
+ <button
462
+ type="button"
463
+ onClick={() => setCurrentView('home')}
464
+ className={clsx(
465
+ 'rounded p-1.5 transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-focus',
466
+ currentView === 'home'
467
+ ? 'bg-card text-accent-fg'
468
+ : 'text-muted hover:bg-surface-hover hover:text-primary'
469
+ )}
470
+ title={t('sidebar.header.homeTitle')}
471
+ aria-label={t('sidebar.header.homeAriaLabel')}
472
+ >
473
+ <Home size={16} />
474
+ </button>
475
+ <span className="text-sm font-medium text-primary">{SESSRUMNIR_PRODUCT_NAME}</span>
476
+ </div>
477
+ <button
478
+ onClick={toggleSidebar}
479
+ className="rounded p-1 text-muted hover:bg-surface-hover hover:text-primary"
480
+ title={t('sidebar.header.closeSidebar')}
481
+ aria-label={t('sidebar.header.closeSidebar')}
482
+ >
483
+ <PanelLeftClose size={16} />
484
+ </button>
485
+ </div>
486
+
487
+ {/* Project + primary action */}
488
+ <div className="border-b border-border pb-3">
489
+ <div className="flex items-center justify-between px-3 pt-3">
490
+ <div className="text-[10px] font-semibold uppercase tracking-[0.14em] text-faint">{t('common.project')}</div>
491
+ <button
492
+ type="button"
493
+ onClick={() => void openProject()}
494
+ className="rounded p-1 text-muted transition-colors hover:bg-highlight hover:text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-focus"
495
+ title={t('sidebar.openProjectFolder.titleWithShortcut')}
496
+ aria-label={t('sidebar.openProjectFolder.ariaLabel')}
497
+ >
498
+ <FolderOpen size={14} />
499
+ </button>
500
+ </div>
501
+ <WorkspaceSwitcher onOpenProject={() => void openProject()} />
502
+ <div className="px-3">
503
+ <button
504
+ type="button"
505
+ onClick={() => void startNewSession()}
506
+ disabled={!activeWorkspace}
507
+ className="group flex w-full items-center gap-2 rounded-lg bg-accent px-3 py-2.5 text-sm font-medium text-white shadow-sm shadow-accent/20 transition-colors hover:bg-accent-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus disabled:cursor-not-allowed disabled:opacity-50"
508
+ title={
509
+ activeWorkspace
510
+ ? t('sidebar.newSessionButton.titleWithShortcut')
511
+ : t('sidebar.newSessionButton.titleNoProject')
512
+ }
513
+ >
514
+ <Plus size={15} className="shrink-0 transition-transform group-hover:rotate-90" />
515
+ <span className="flex-1 text-left">{t('sidebar.newSessionButton.label')}</span>
516
+ <kbd className="rounded border border-white/20 bg-white/10 px-1.5 py-0.5 text-[10px] font-medium text-white/75">{t('sidebar.newSessionButton.shortcutKbd')}</kbd>
517
+ </button>
518
+ <div className="mt-1.5 px-1 text-[11px] text-faint">
519
+ {activeWorkspace
520
+ ? t('sidebar.newSessionButton.startsIn', { project: activeWorkspace.name })
521
+ : t('sidebar.newSessionButton.openProjectToBegin')}
522
+ </div>
523
+ </div>
524
+ </div>
525
+
526
+ {/* Navigation */}
527
+ <nav className="space-y-3 border-b border-border px-2 py-3">
528
+ <div>
529
+ <div className="mb-1 px-3 text-[10px] font-semibold uppercase tracking-[0.14em] text-faint">{t('sidebar.nav.workspaceSection')}</div>
530
+ <div className="space-y-0.5">
531
+ <SidebarItem
532
+ icon={<MessageSquare size={14} />}
533
+ label={t('sidebar.nav.chat')}
534
+ active={currentView === 'chat'}
535
+ onClick={() => setCurrentView('chat')}
536
+ />
537
+ <SidebarItem
538
+ icon={<FolderOpen size={14} />}
539
+ label={t('sidebar.nav.sessions')}
540
+ active={currentView === 'sessions'}
541
+ onClick={() => {
542
+ setSessionsScope('current')
543
+ setCurrentView('sessions')
544
+ }}
545
+ title={t('sidebar.nav.sessionsTitle')}
546
+ />
547
+ <SidebarItem
548
+ icon={<Plus size={14} />}
549
+ label={t('missionControl.newTask')}
550
+ active={false}
551
+ onClick={() => setTaskLauncherOpen(true)}
552
+ title={t('sidebar.nav.newTaskTitle')}
553
+ />
554
+ </div>
555
+ </div>
556
+ <div>
557
+ <div className="mb-1 flex items-center gap-1 px-3 text-[10px] font-semibold uppercase tracking-[0.14em] text-faint">
558
+ <span>{t('sidebar.nav.activitySection')}</span>
559
+ {activeWorkspace && (
560
+ <span className="min-w-0 truncate normal-case">· {activeWorkspace.name}</span>
561
+ )}
562
+ </div>
563
+ <div className="space-y-0.5">
564
+ <SidebarItem
565
+ icon={<LayoutDashboard size={14} />}
566
+ label={t('sidebar.nav.missionControl')}
567
+ active={currentView === 'mission-control'}
568
+ onClick={() => {
569
+ setWorkflowPanelOpen(false)
570
+ setCurrentView('mission-control')
571
+ }}
572
+ title={t('sidebar.nav.missionControlTitle')}
573
+ />
574
+ <SidebarItem
575
+ icon={<WorkflowIcon size={14} />}
576
+ label={t('sidebar.nav.workflows')}
577
+ // Only highlighted when THIS project's scope is open, so it never
578
+ // fights the Tools' global "All Workflows" entry. With no project
579
+ // open the entry falls back to the global list (same as the old
580
+ // behavior) but stays unhighlighted.
581
+ active={
582
+ !!activeWorkspace &&
583
+ workflowPanelOpen &&
584
+ !workflowPanelFilter &&
585
+ workflowPanelWorkspaceId === activeWorkspace.id
586
+ }
587
+ onClick={() => openWorkflowRunsForWorkspace(activeWorkspace?.id ?? null)}
588
+ title={
589
+ activeWorkspace
590
+ ? t('sidebar.nav.workflowsTitleWithProject', { project: activeWorkspace.name })
591
+ : t('sidebar.nav.workflowsTitleNoProject')
592
+ }
593
+ />
594
+ <SidebarItem
595
+ icon={<Activity size={14} />}
596
+ label={t('sidebar.nav.timeline')}
597
+ active={currentView === 'timeline'}
598
+ onClick={() => setCurrentView('timeline')}
599
+ title={
600
+ activeWorkspace
601
+ ? t('sidebar.nav.timelineTitleWithProject', { project: activeWorkspace.name })
602
+ : t('sidebar.nav.timeline')
603
+ }
604
+ />
605
+ </div>
606
+ </div>
607
+ </nav>
608
+
609
+ {/* Current session info */}
610
+ {sessionState && (
611
+ renamingWhere === 'current' ? (
612
+ <div className="mx-3 mt-2 rounded-md bg-surface p-3">
613
+ <div className="text-xs font-medium text-muted uppercase tracking-wider">{t('sidebar.currentSession.heading')}</div>
614
+ <div className="mt-1.5 flex">{renderRenameInput()}</div>
615
+ {sessionState.model && (
616
+ <div className="mt-1 truncate text-xs text-dim">{sessionState.model.name}</div>
617
+ )}
618
+ <div className="mt-1 text-xs text-dim">{t('sidebar.currentSession.messageCount', { count: sessionState.messageCount })}</div>
619
+ </div>
620
+ ) : (
621
+ <div className="group relative mx-3 mt-2">
622
+ <button
623
+ type="button"
624
+ onClick={() => setCurrentView('chat')}
625
+ onDoubleClick={() => startSessionRename('current')}
626
+ onContextMenu={handleCurrentSessionRightClick}
627
+ className="w-full rounded-md bg-surface p-3 pr-9 text-left transition-colors hover:bg-surface-hover focus:outline-none focus:ring-1 focus:ring-border-strong"
628
+ title={t('sidebar.currentSession.openTitle')}
629
+ >
630
+ <div className="text-xs font-medium text-muted uppercase tracking-wider">{t('sidebar.currentSession.heading')}</div>
631
+ <div className="mt-1.5 text-sm text-primary truncate">
632
+ {getSessionTitle(sessionState.sessionName, sessionState.sessionId, currentSessionPreview)}
633
+ </div>
634
+ {sessionState.model && (
635
+ <div className="mt-1 truncate text-xs text-dim">
636
+ {sessionState.model.name}
637
+ </div>
638
+ )}
639
+ <div className="mt-1 text-xs text-dim">
640
+ {t('sidebar.currentSession.messageCount', { count: sessionState.messageCount })}
641
+ </div>
642
+ </button>
643
+ {/* Sibling overlay — the panel above stays a single non-nested button. */}
644
+ <button
645
+ type="button"
646
+ onClick={() => openWorkflowRunsForSession(sessionState.sessionId)}
647
+ className="absolute right-2 top-3 rounded p-1.5 text-faint opacity-0 transition-opacity hover:bg-highlight hover:text-accent-fg focus-visible:opacity-100 group-hover:opacity-100"
648
+ title={t('sidebar.workflowRunsForSession')}
649
+ aria-label={t('sidebar.workflowRunsForSession')}
650
+ >
651
+ <WorkflowIcon size={13} />
652
+ </button>
653
+ </div>
654
+ )
655
+ )}
656
+
657
+ {/* Recent sessions for the active project. Cross-project history stays in Sessions. */}
658
+ <div className="min-h-0 flex-1 overflow-y-auto px-2 py-3">
659
+ <div className="mb-1 flex items-center justify-between px-2">
660
+ <div className="text-[10px] font-semibold uppercase tracking-[0.14em] text-faint">
661
+ {activeWorkspace
662
+ ? t('sidebar.recentSessions.headingWithProject', { project: activeWorkspace.name })
663
+ : t('sidebar.recentSessions.headingNoProject')}
664
+ </div>
665
+ <button
666
+ type="button"
667
+ onClick={() => {
668
+ setSessionsScope('all')
669
+ setCurrentView('sessions')
670
+ }}
671
+ className="text-[11px] text-muted transition-colors hover:text-accent-fg focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-focus"
672
+ >
673
+ {t('sidebar.recentSessions.viewAll')}
674
+ </button>
675
+ </div>
676
+ {activeWorkspace ? (
677
+ recentSessionsForWorkspace.length === 0 ? (
678
+ <div className="mx-2 mt-2 rounded-lg border border-dashed border-border px-3 py-4 text-center text-xs text-faint">
679
+ {t('sidebar.recentSessions.emptyInProject')}
680
+ <button
681
+ type="button"
682
+ onClick={() => void startNewSession()}
683
+ className="mt-1 block w-full text-accent-fg transition-colors hover:text-accent"
684
+ >
685
+ {t('sidebar.recentSessions.startOneNow')}
686
+ </button>
687
+ </div>
688
+ ) : (
689
+ <div className="space-y-0.5">
690
+ {recentSessionsForWorkspace.map((session) => renderSessionRow(session))}
691
+ </div>
692
+ )
693
+ ) : recentGroups.length === 0 ? (
694
+ <div className="mx-2 mt-2 rounded-lg border border-dashed border-border px-3 py-4 text-center text-xs text-faint">
695
+ {t('sidebar.recentSessions.emptyNoProject')}
696
+ </div>
697
+ ) : (
698
+ recentGroups.map(renderRecentGroup)
699
+ )}
700
+ </div>
701
+
702
+ {/* Archived sessions (collapsible) */}
703
+ {archivedList.length > 0 && (
704
+ <div className="shrink-0 border-t border-border px-2 py-1">
705
+ <button
706
+ onClick={() => setArchivedOpen((open) => !open)}
707
+ className="flex w-full items-center gap-1.5 rounded px-2 py-1.5 text-xs font-medium uppercase tracking-wider text-dim hover:text-secondary transition-colors"
708
+ title={archivedOpen ? t('sidebar.archived.collapseTitle') : t('sidebar.archived.expandTitle')}
709
+ >
710
+ <ChevronDown
711
+ size={12}
712
+ className={clsx('shrink-0 transition-transform', !archivedOpen && '-rotate-90')}
713
+ />
714
+ <Archive size={12} className="shrink-0" />
715
+ <span>{t('sidebar.archived.heading', { count: archivedList.length })}</span>
716
+ </button>
717
+ {archivedOpen && (
718
+ <div className="max-h-48 overflow-y-auto pb-1">
719
+ {archivedList.map((session) => renderSessionRow(session))}
720
+ </div>
721
+ )}
722
+ </div>
723
+ )}
724
+
725
+ {/* Secondary tools stay available without competing with project/session work. */}
726
+ <div className="shrink-0 border-t border-border px-2 py-2">
727
+ <div className="mb-1 px-3 text-[10px] font-semibold uppercase tracking-[0.14em] text-faint">{t('sidebar.tools.sectionLabel')}</div>
728
+ <div className="grid grid-cols-2 gap-0.5">
729
+ <SidebarItem
730
+ compact
731
+ icon={<Package size={13} />}
732
+ label={t('sidebar.tools.packages')}
733
+ active={toolViewShowing('packages')}
734
+ onClick={() => openToolView('packages')}
735
+ />
736
+ <SidebarItem
737
+ compact
738
+ icon={<StickyNote size={13} />}
739
+ label={t('sidebar.tools.notes')}
740
+ active={toolViewShowing('notes')}
741
+ onClick={() => openToolView('notes')}
742
+ />
743
+ <SidebarItem
744
+ compact
745
+ icon={<Sparkles size={13} />}
746
+ label={t('common.skills')}
747
+ active={toolViewShowing('skills')}
748
+ onClick={() => openToolView('skills')}
749
+ />
750
+ <SidebarItem
751
+ compact
752
+ icon={<Stethoscope size={13} />}
753
+ label={t('sidebar.tools.diagnostics')}
754
+ active={toolViewShowing('diagnostics')}
755
+ onClick={() => openToolView('diagnostics')}
756
+ />
757
+ {/* Global workflow list lives here — "browse everything" territory —
758
+ so the Activity section stays scoped to the current project. */}
759
+ <SidebarItem
760
+ compact
761
+ icon={<WorkflowIcon size={13} />}
762
+ label={t('sidebar.tools.allWorkflows')}
763
+ // Highlighted only when the global scope is open (never alongside
764
+ // the Activity entry, whose active state requires a workspace id).
765
+ active={globalWorkflowOpen}
766
+ onClick={() => openWorkflowRunsForWorkspace(null)}
767
+ title={t('sidebar.tools.allWorkflowsTitle')}
768
+ />
769
+ <SidebarItem
770
+ compact
771
+ icon={<Settings size={13} />}
772
+ label={t('common.settings')}
773
+ active={toolViewShowing('settings')}
774
+ onClick={() => openToolView('settings')}
775
+ />
776
+ </div>
777
+ </div>
778
+ {SessionMenu}
779
+ </aside>
780
+ <ResizeHandle
781
+ onResize={applyResizeDelta}
782
+ onResizeEnd={() => void saveSidebarWidth(widthRef.current)}
783
+ />
784
+ </>
785
+ )
786
+ }
787
+
788
+ // ─── Workspace Switcher ──────────────────────────────────────────────────────
789
+
790
+ function WorkspaceSwitcher({ onOpenProject }: { onOpenProject: () => void }): React.JSX.Element {
791
+ const { t } = useTranslation()
792
+ const workspaces = useAppStore((state) => state.workspaces)
793
+ const activeWorkspace = useAppStore((state) => state.activeWorkspace)
794
+ const activateWorkspace = useAppStore((state) => state.activateWorkspace)
795
+ const removeWorkspace = useAppStore((state) => state.removeWorkspace)
796
+ const renameWorkspace = useAppStore((state) => state.renameWorkspace)
797
+ const changeWorkspaceFolder = useAppStore((state) => state.changeWorkspaceFolder)
798
+ const pendingPromptCounts = useAppStore((state) => state.pendingPromptCounts)
799
+ const workspaceActivity = useAppStore((state) => state.workspaceActivity)
800
+ const { show: showContextMenu, ContextMenuComponent: WorkspaceContextMenu } = useContextMenu()
801
+
802
+ // Prompts held for workspaces other than the active one — the active
803
+ // workspace's prompt is already on screen, so only elsewhere needs a badge.
804
+ const promptsWaitingElsewhere = countPromptsWaitingElsewhere(
805
+ pendingPromptCounts,
806
+ activeWorkspace?.id ?? null
807
+ )
808
+
809
+ // Background work in non-active workspaces, condensed to one header dot.
810
+ const backgroundActivity = summarizeBackgroundActivity(
811
+ workspaceActivity,
812
+ activeWorkspace?.id ?? null
813
+ )
814
+
815
+ const [isOpen, setIsOpen] = useState(false)
816
+ const [isRenaming, setIsRenaming] = useState(false)
817
+ const [newName, setNewName] = useState('')
818
+
819
+ const handleRename = async () => {
820
+ if (!activeWorkspace || !newName.trim()) return
821
+ await renameWorkspace(activeWorkspace.id, newName.trim())
822
+ setIsRenaming(false)
823
+ }
824
+
825
+ const startRenaming = () => {
826
+ setNewName(activeWorkspace?.name ?? '')
827
+ setIsRenaming(true)
828
+ setIsOpen(false)
829
+ }
830
+
831
+ const handleChangeFolder = async () => {
832
+ if (!activeWorkspace) return
833
+ const path = await window.piDesktop.system.openDialog({ title: t('dialogs.selectWorkspaceFolder.title') })
834
+ if (path) await changeWorkspaceFolder(activeWorkspace.id, path)
835
+ }
836
+
837
+ const handleWorkspaceContextMenu = (e: React.MouseEvent) => {
838
+ e.preventDefault()
839
+ e.stopPropagation()
840
+ showContextMenu(e, [
841
+ {
842
+ id: 'rename',
843
+ label: t('sidebar.workspaceMenu.rename'),
844
+ icon: <Pencil size={14} />,
845
+ disabled: !activeWorkspace,
846
+ action: startRenaming,
847
+ },
848
+ {
849
+ id: 'change-folder',
850
+ label: t('sidebar.workspaceMenu.changeFolder'),
851
+ icon: <FolderOpen size={14} />,
852
+ disabled: !activeWorkspace,
853
+ action: () => {
854
+ void handleChangeFolder()
855
+ },
856
+ },
857
+ ])
858
+ }
859
+
860
+ return (
861
+ <div className="px-3 py-2">
862
+ {/* Current workspace */}
863
+ {isRenaming ? (
864
+ <div className="flex items-center gap-2 rounded-md bg-surface px-3 py-2">
865
+ <Layers size={14} style={{ color: activeWorkspace?.color ?? '#6b7280' }} />
866
+ <input
867
+ type="text"
868
+ value={newName}
869
+ onChange={(e) => setNewName(e.target.value)}
870
+ onKeyDown={(e) => {
871
+ if (e.key === 'Enter') {
872
+ e.preventDefault()
873
+ handleRename()
874
+ } else if (e.key === 'Escape') {
875
+ setIsRenaming(false)
876
+ }
877
+ }}
878
+ onBlur={handleRename}
879
+ placeholder={t('sidebar.workspaceSwitcher.namePlaceholder')}
880
+ className="min-w-0 flex-1 rounded border border-border-strong bg-card px-2 py-1 text-sm text-primary placeholder:text-faint focus:border-focus focus:outline-none"
881
+ autoFocus
882
+ />
883
+ </div>
884
+ ) : (
885
+ <button
886
+ onClick={() => setIsOpen(!isOpen)}
887
+ onDoubleClick={startRenaming}
888
+ onContextMenu={handleWorkspaceContextMenu}
889
+ title={t('sidebar.workspaceSwitcher.triggerTitle')}
890
+ className="flex w-full items-center justify-between rounded-md px-3 py-2 text-sm text-primary hover:bg-surface-hover transition-colors"
891
+ >
892
+ <div className="flex min-w-0 items-center gap-2 text-left">
893
+ <Layers size={14} className="shrink-0" style={{ color: activeWorkspace?.color ?? '#6b7280' }} />
894
+ <div className="min-w-0">
895
+ <div className="truncate text-sm">{activeWorkspace?.name ?? t('sidebar.workspaceSwitcher.noWorkspace')}</div>
896
+ {activeWorkspace && (
897
+ <div className="truncate text-[10px] text-faint">{activeWorkspace.path}</div>
898
+ )}
899
+ </div>
900
+ </div>
901
+ <div className="flex shrink-0 items-center gap-1.5">
902
+ {backgroundActivity && (
903
+ <span
904
+ className={clsx(
905
+ 'h-2 w-2 rounded-full',
906
+ backgroundActivity.colorClass,
907
+ backgroundActivity.pulse && 'animate-pulse'
908
+ )}
909
+ title={backgroundActivity.label}
910
+ />
911
+ )}
912
+ {promptsWaitingElsewhere > 0 && (
913
+ <span
914
+ className="rounded bg-warning-bg px-1.5 py-0.5 text-[10px] text-warning"
915
+ title={t('sidebar.promptsWaitingInOtherWorkspaces', { count: promptsWaitingElsewhere })}
916
+ >
917
+ {promptsWaitingElsewhere}
918
+ </span>
919
+ )}
920
+ <ChevronDown
921
+ size={14}
922
+ className={clsx(
923
+ 'text-dim transition-transform',
924
+ isOpen && 'rotate-180'
925
+ )}
926
+ />
927
+ </div>
928
+ </button>
929
+ )}
930
+ {WorkspaceContextMenu}
931
+
932
+ {/* Dropdown */}
933
+ {isOpen && (
934
+ <div className="mt-1 rounded-md border border-border bg-surface py-1 animate-fade-in">
935
+ {/* Workspace list */}
936
+ {workspaces.map((ws) => (
937
+ <div
938
+ key={ws.id}
939
+ className="group flex items-center justify-between px-3 py-1.5 hover:bg-surface-hover"
940
+ >
941
+ <button
942
+ onClick={() => {
943
+ void activateWorkspace(ws.id)
944
+ setIsOpen(false)
945
+ }}
946
+ className="flex items-center gap-2 min-w-0 flex-1 text-left"
947
+ >
948
+ <div
949
+ className="h-2 w-2 rounded-full shrink-0"
950
+ style={{ backgroundColor: ws.color }}
951
+ />
952
+ <span className="text-sm text-secondary truncate">{ws.name}</span>
953
+ {ws.id === activeWorkspace?.id && (
954
+ <Check size={12} className="shrink-0 text-success" />
955
+ )}
956
+ {ws.id !== activeWorkspace?.id && (pendingPromptCounts[ws.id] ?? 0) > 0 && (
957
+ <span
958
+ className="shrink-0 rounded bg-warning-bg px-1.5 py-0.5 text-[10px] text-warning"
959
+ title={formatPromptsWaiting(pendingPromptCounts[ws.id])}
960
+ >
961
+ {pendingPromptCounts[ws.id]}
962
+ </span>
963
+ )}
964
+ {(() => {
965
+ const indicator = workspaceActivityIndicator(workspaceActivity[ws.id])
966
+ return indicator ? (
967
+ <span
968
+ className={clsx(
969
+ 'h-2 w-2 shrink-0 rounded-full',
970
+ indicator.colorClass,
971
+ indicator.pulse && 'animate-pulse'
972
+ )}
973
+ title={indicator.label}
974
+ />
975
+ ) : null
976
+ })()}
977
+ </button>
978
+ {workspaces.length > 1 && (
979
+ <button
980
+ onClick={(e) => {
981
+ e.stopPropagation()
982
+ removeWorkspace(ws.id)
983
+ }}
984
+ className="rounded p-1 text-faint opacity-0 group-hover:opacity-100 hover:text-error transition-all"
985
+ title={t('store.confirm.removeWorkspaceTitle')}
986
+ aria-label={t('store.confirm.removeWorkspaceTitle')}
987
+ >
988
+ <Trash2 size={12} />
989
+ </button>
990
+ )}
991
+ </div>
992
+ ))}
993
+
994
+ <button
995
+ type="button"
996
+ onClick={() => {
997
+ setIsOpen(false)
998
+ onOpenProject()
999
+ }}
1000
+ className="flex w-full items-center gap-2 border-t border-border px-3 py-2 text-xs text-muted transition-colors hover:bg-surface-hover hover:text-secondary"
1001
+ >
1002
+ <FolderOpen size={12} />
1003
+ {t('sidebar.workspaceSwitcher.openProjectEllipsis')}
1004
+ </button>
1005
+ </div>
1006
+ )}
1007
+ </div>
1008
+ )
1009
+ }
1010
+
1011
+ // ─── Sidebar Item ────────────────────────────────────────────────────────────
1012
+
1013
+ function SidebarItem({
1014
+ icon,
1015
+ label,
1016
+ active,
1017
+ onClick,
1018
+ compact = false,
1019
+ title,
1020
+ }: {
1021
+ icon: React.ReactNode
1022
+ label: string
1023
+ active: boolean
1024
+ onClick: () => void
1025
+ compact?: boolean
1026
+ title?: string
1027
+ }): React.JSX.Element {
1028
+ return (
1029
+ <button
1030
+ type="button"
1031
+ onClick={onClick}
1032
+ title={title}
1033
+ className={clsx(
1034
+ 'flex w-full items-center gap-2 rounded-md transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-focus',
1035
+ compact ? 'px-2 py-1.5 text-xs' : 'px-3 py-2 text-sm',
1036
+ active
1037
+ ? 'bg-card text-primary'
1038
+ : 'text-muted hover:bg-highlight hover:text-secondary'
1039
+ )}
1040
+ >
1041
+ {icon}
1042
+ <span className="truncate">{label}</span>
1043
+ </button>
1044
+ )
1045
+ }