@slycode/slycode 0.2.21 → 0.2.23

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 (271) hide show
  1. package/dist/bridge/api.d.ts +2 -1
  2. package/dist/bridge/api.js +114 -1
  3. package/dist/bridge/api.js.map +1 -1
  4. package/dist/bridge/git-utils.d.ts +9 -0
  5. package/dist/bridge/git-utils.js +49 -0
  6. package/dist/bridge/git-utils.js.map +1 -0
  7. package/dist/bridge/index.js +8 -2
  8. package/dist/bridge/index.js.map +1 -1
  9. package/dist/bridge/provider-utils.d.ts +10 -0
  10. package/dist/bridge/provider-utils.js +5 -1
  11. package/dist/bridge/provider-utils.js.map +1 -1
  12. package/dist/bridge/response-store.d.ts +46 -0
  13. package/dist/bridge/response-store.js +95 -0
  14. package/dist/bridge/response-store.js.map +1 -0
  15. package/dist/bridge/session-manager.d.ts +33 -1
  16. package/dist/bridge/session-manager.js +191 -2
  17. package/dist/bridge/session-manager.js.map +1 -1
  18. package/dist/bridge/types.d.ts +41 -0
  19. package/dist/data/scaffold-templates/tutorial-project/documentation/kanban.json +1 -1
  20. package/dist/messaging/bridge-client.d.ts +7 -3
  21. package/dist/messaging/bridge-client.js +26 -5
  22. package/dist/messaging/bridge-client.js.map +1 -1
  23. package/dist/messaging/index.js +149 -30
  24. package/dist/messaging/index.js.map +1 -1
  25. package/dist/messaging/state.d.ts +3 -0
  26. package/dist/messaging/state.js +13 -0
  27. package/dist/messaging/state.js.map +1 -1
  28. package/dist/messaging/types.d.ts +3 -0
  29. package/dist/scripts/kanban.js +448 -2
  30. package/dist/store/actions/checkpoint.md +1 -1
  31. package/dist/store/actions/context.md +1 -1
  32. package/dist/store/actions/create-card.md +1 -1
  33. package/dist/store/actions/deep-design.md +13 -3
  34. package/dist/store/actions/design-requirements.md +11 -1
  35. package/dist/store/actions/explore.md +1 -1
  36. package/dist/store/actions/onboard.md +2 -4
  37. package/dist/store/actions/summarize.md +1 -1
  38. package/dist/store/skills/kanban/SKILL.md +77 -3
  39. package/dist/web/.next/BUILD_ID +1 -1
  40. package/dist/web/.next/app-path-routes-manifest.json +1 -0
  41. package/dist/web/.next/build-manifest.json +2 -2
  42. package/dist/web/.next/prerender-manifest.json +3 -3
  43. package/dist/web/.next/routes-manifest.json +6 -0
  44. package/dist/web/.next/server/app/_global-error.html +2 -2
  45. package/dist/web/.next/server/app/_global-error.rsc +1 -1
  46. package/dist/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  47. package/dist/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  48. package/dist/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  49. package/dist/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  50. package/dist/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  51. package/dist/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  52. package/dist/web/.next/server/app/_not-found.html +1 -1
  53. package/dist/web/.next/server/app/_not-found.rsc +10 -10
  54. package/dist/web/.next/server/app/_not-found.segments/_full.segment.rsc +10 -10
  55. package/dist/web/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  56. package/dist/web/.next/server/app/_not-found.segments/_index.segment.rsc +5 -5
  57. package/dist/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  58. package/dist/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  59. package/dist/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  60. package/dist/web/.next/server/app/api/areas/route.js +1 -1
  61. package/dist/web/.next/server/app/api/areas/route.js.nft.json +1 -1
  62. package/dist/web/.next/server/app/api/bridge/[...path]/route.js +1 -1
  63. package/dist/web/.next/server/app/api/bridge/[...path]/route.js.nft.json +1 -1
  64. package/dist/web/.next/server/app/api/changelog/route/app-paths-manifest.json +3 -0
  65. package/dist/web/.next/server/app/api/changelog/route/build-manifest.json +11 -0
  66. package/dist/web/.next/server/app/api/changelog/route/server-reference-manifest.json +4 -0
  67. package/dist/web/.next/server/app/api/changelog/route.js +7 -0
  68. package/dist/web/.next/server/app/api/changelog/route.js.map +5 -0
  69. package/dist/web/.next/server/app/api/changelog/route.js.nft.json +1 -0
  70. package/dist/web/.next/server/app/api/changelog/route_client-reference-manifest.js +2 -0
  71. package/dist/web/.next/server/app/api/cli-assets/assistant/route.js +1 -1
  72. package/dist/web/.next/server/app/api/cli-assets/assistant/route.js.nft.json +1 -1
  73. package/dist/web/.next/server/app/api/cli-assets/fix/route.js +1 -1
  74. package/dist/web/.next/server/app/api/cli-assets/fix/route.js.nft.json +1 -1
  75. package/dist/web/.next/server/app/api/cli-assets/import/route.js +1 -1
  76. package/dist/web/.next/server/app/api/cli-assets/import/route.js.nft.json +1 -1
  77. package/dist/web/.next/server/app/api/cli-assets/route.js +2 -2
  78. package/dist/web/.next/server/app/api/cli-assets/route.js.nft.json +1 -1
  79. package/dist/web/.next/server/app/api/cli-assets/store/preview/route.js +1 -1
  80. package/dist/web/.next/server/app/api/cli-assets/store/preview/route.js.nft.json +1 -1
  81. package/dist/web/.next/server/app/api/cli-assets/store/route.js +2 -2
  82. package/dist/web/.next/server/app/api/cli-assets/store/route.js.nft.json +1 -1
  83. package/dist/web/.next/server/app/api/cli-assets/sync/route.js +1 -1
  84. package/dist/web/.next/server/app/api/cli-assets/sync/route.js.nft.json +1 -1
  85. package/dist/web/.next/server/app/api/cli-assets/updates/route.js +2 -2
  86. package/dist/web/.next/server/app/api/cli-assets/updates/route.js.nft.json +1 -1
  87. package/dist/web/.next/server/app/api/dashboard/route.js +2 -2
  88. package/dist/web/.next/server/app/api/dashboard/route.js.nft.json +1 -1
  89. package/dist/web/.next/server/app/api/events/route.js +1 -1
  90. package/dist/web/.next/server/app/api/events/route.js.nft.json +1 -1
  91. package/dist/web/.next/server/app/api/file/route.js +1 -1
  92. package/dist/web/.next/server/app/api/file/route.js.nft.json +1 -1
  93. package/dist/web/.next/server/app/api/git-status/route.js +1 -1
  94. package/dist/web/.next/server/app/api/git-status/route.js.nft.json +1 -1
  95. package/dist/web/.next/server/app/api/kanban/route.js +1 -1
  96. package/dist/web/.next/server/app/api/kanban/route.js.nft.json +1 -1
  97. package/dist/web/.next/server/app/api/kanban/stream/route.js +1 -1
  98. package/dist/web/.next/server/app/api/kanban/stream/route.js.nft.json +1 -1
  99. package/dist/web/.next/server/app/api/projects/[id]/route.js +2 -2
  100. package/dist/web/.next/server/app/api/projects/[id]/route.js.nft.json +1 -1
  101. package/dist/web/.next/server/app/api/projects/analyze/route.js +1 -1
  102. package/dist/web/.next/server/app/api/projects/analyze/route.js.nft.json +1 -1
  103. package/dist/web/.next/server/app/api/projects/reorder/route.js +2 -2
  104. package/dist/web/.next/server/app/api/projects/reorder/route.js.nft.json +1 -1
  105. package/dist/web/.next/server/app/api/projects/route.js +2 -2
  106. package/dist/web/.next/server/app/api/projects/route.js.nft.json +1 -1
  107. package/dist/web/.next/server/app/api/providers/route.js +1 -1
  108. package/dist/web/.next/server/app/api/providers/route.js.nft.json +1 -1
  109. package/dist/web/.next/server/app/api/scheduler/route.js +1 -1
  110. package/dist/web/.next/server/app/api/scheduler/route.js.nft.json +1 -1
  111. package/dist/web/.next/server/app/api/search/route.js +1 -1
  112. package/dist/web/.next/server/app/api/search/route.js.nft.json +1 -1
  113. package/dist/web/.next/server/app/api/settings/route.js +1 -1
  114. package/dist/web/.next/server/app/api/settings/route.js.nft.json +1 -1
  115. package/dist/web/.next/server/app/api/sly-actions/invalidate/route.js +1 -1
  116. package/dist/web/.next/server/app/api/sly-actions/invalidate/route.js.nft.json +1 -1
  117. package/dist/web/.next/server/app/api/sly-actions/route.js +1 -1
  118. package/dist/web/.next/server/app/api/sly-actions/route.js.nft.json +1 -1
  119. package/dist/web/.next/server/app/api/sly-actions/stream/route.js +1 -1
  120. package/dist/web/.next/server/app/api/sly-actions/stream/route.js.nft.json +1 -1
  121. package/dist/web/.next/server/app/api/system-stats/route.js +1 -1
  122. package/dist/web/.next/server/app/api/system-stats/route.js.nft.json +1 -1
  123. package/dist/web/.next/server/app/api/terminal-classes/route.js +1 -1
  124. package/dist/web/.next/server/app/api/terminal-classes/route.js.nft.json +1 -1
  125. package/dist/web/.next/server/app/api/transcribe/route.js +5 -5
  126. package/dist/web/.next/server/app/api/transcribe/route.js.nft.json +1 -1
  127. package/dist/web/.next/server/app/api/version-check/route.js +1 -1
  128. package/dist/web/.next/server/app/api/version-check/route.js.nft.json +1 -1
  129. package/dist/web/.next/server/app/page.js +1 -1
  130. package/dist/web/.next/server/app/page.js.nft.json +1 -1
  131. package/dist/web/.next/server/app/page_client-reference-manifest.js +1 -1
  132. package/dist/web/.next/server/app/project/[id]/page.js +2 -2
  133. package/dist/web/.next/server/app/project/[id]/page.js.nft.json +1 -1
  134. package/dist/web/.next/server/app/project/[id]/page_client-reference-manifest.js +1 -1
  135. package/dist/web/.next/server/app-paths-manifest.json +1 -0
  136. package/dist/web/.next/server/chunks/{[externals]__c6831f39._.js → [externals]__78e522ea._.js} +2 -2
  137. package/dist/web/.next/server/chunks/{[root-of-the-server]__1ec21ccc._.js → [root-of-the-server]__029203cd._.js} +3 -3
  138. package/dist/web/.next/server/chunks/{[root-of-the-server]__4297cb97._.js → [root-of-the-server]__0d6d4443._.js} +1 -1
  139. package/dist/web/.next/server/chunks/[root-of-the-server]__172ad0b1._.js +18 -0
  140. package/dist/web/.next/server/chunks/[root-of-the-server]__1c5f4ef9._.js +3 -0
  141. package/dist/web/.next/server/chunks/[root-of-the-server]__1cab11f0._.js +3 -0
  142. package/dist/web/.next/server/chunks/{[root-of-the-server]__0f69c28a._.js → [root-of-the-server]__1eb3f172._.js} +2 -2
  143. package/dist/web/.next/server/chunks/[root-of-the-server]__22cba275._.js +3 -0
  144. package/dist/web/.next/server/chunks/[root-of-the-server]__2543e413._.js +3 -0
  145. package/dist/web/.next/server/chunks/[root-of-the-server]__2c42a835._.js +3 -0
  146. package/dist/web/.next/server/chunks/[root-of-the-server]__2ed0ff47._.js +3 -0
  147. package/dist/web/.next/server/chunks/[root-of-the-server]__35454eea._.js +27 -0
  148. package/dist/web/.next/server/chunks/[root-of-the-server]__35768b56._.js +3 -0
  149. package/dist/web/.next/server/chunks/[root-of-the-server]__3880228a._.js +3 -0
  150. package/dist/web/.next/server/chunks/[root-of-the-server]__42322d88._.js +3 -0
  151. package/dist/web/.next/server/chunks/{[root-of-the-server]__d0f4efec._.js → [root-of-the-server]__5152eeff._.js} +3 -3
  152. package/dist/web/.next/server/chunks/[root-of-the-server]__527c7f57._.js +3 -0
  153. package/dist/web/.next/server/chunks/[root-of-the-server]__5c5dac4b._.js +3 -0
  154. package/dist/web/.next/server/chunks/[root-of-the-server]__5cb130f2._.js +3 -0
  155. package/dist/web/.next/server/chunks/[root-of-the-server]__68927e75._.js +3 -0
  156. package/dist/web/.next/server/chunks/[root-of-the-server]__719517c7._.js +3 -0
  157. package/dist/web/.next/server/chunks/[root-of-the-server]__73cf49c2._.js +3 -0
  158. package/dist/web/.next/server/chunks/{[root-of-the-server]__f5dae2ad._.js → [root-of-the-server]__7af4ab09._.js} +1 -1
  159. package/dist/web/.next/server/chunks/{[root-of-the-server]__4244617a._.js → [root-of-the-server]__7e6860e0._.js} +3 -3
  160. package/dist/web/.next/server/chunks/[root-of-the-server]__88bf5e22._.js +3 -0
  161. package/dist/web/.next/server/chunks/{[root-of-the-server]__f97e93fa._.js → [root-of-the-server]__8b4259cb._.js} +3 -3
  162. package/dist/web/.next/server/chunks/[root-of-the-server]__92f81907._.js +3 -0
  163. package/dist/web/.next/server/chunks/[root-of-the-server]__967603e9._.js +3 -0
  164. package/dist/web/.next/server/chunks/[root-of-the-server]__9e4bd28f._.js +3 -0
  165. package/dist/web/.next/server/chunks/[root-of-the-server]__a259539f._.js +3 -0
  166. package/dist/web/.next/server/chunks/[root-of-the-server]__ba1d2e56._.js +3 -0
  167. package/dist/web/.next/server/chunks/[root-of-the-server]__c942d872._.js +3 -0
  168. package/dist/web/.next/server/chunks/[root-of-the-server]__d7893622._.js +3 -0
  169. package/dist/web/.next/server/chunks/{[root-of-the-server]__3b9d3e43._.js → [root-of-the-server]__d843611b._.js} +6 -6
  170. package/dist/web/.next/server/chunks/[root-of-the-server]__f4d2627f._.js +3 -0
  171. package/dist/web/.next/server/chunks/[root-of-the-server]__f597835d._.js +3 -0
  172. package/dist/web/.next/server/chunks/{[root-of-the-server]__cf14e306._.js → [root-of-the-server]__fe8b9abd._.js} +1 -1
  173. package/dist/web/.next/server/chunks/_next-internal_server_app_api_changelog_route_actions_d6e239bf.js +3 -0
  174. package/dist/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_18324462.js +1 -1
  175. package/dist/web/.next/server/chunks/src_677020aa._.js +1 -1
  176. package/dist/web/.next/server/chunks/src_lib_scheduler_ts_03988e3e._.js +1 -1
  177. package/dist/web/.next/server/chunks/src_lib_scheduler_ts_7120457c._.js +1 -1
  178. package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__1f5fc489._.js +4 -3
  179. package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__43d93717._.js +3 -0
  180. package/dist/web/.next/server/chunks/ssr/{[root-of-the-server]__077f472c._.js → [root-of-the-server]__6183d28c._.js} +1 -1
  181. package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__90f82e6d._.js +3 -0
  182. package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__bcbe4bf2._.js +4 -3
  183. package/dist/web/.next/server/chunks/ssr/src_components_Dashboard_tsx_efc4dc27._.js +1 -1
  184. package/dist/web/.next/server/chunks/ssr/src_components_c4135402._.js +1 -1
  185. package/dist/web/.next/server/chunks/ssr/src_contexts_VoiceContext_tsx_cfba7292._.js +1 -1
  186. package/dist/web/.next/server/chunks/ssr/src_lib_registry_ts_2fc87c9c._.js +1 -1
  187. package/dist/web/.next/server/pages/404.html +1 -1
  188. package/dist/web/.next/server/pages/500.html +2 -2
  189. package/dist/web/.next/server/server-reference-manifest.js +1 -1
  190. package/dist/web/.next/server/server-reference-manifest.json +1 -1
  191. package/dist/web/.next/static/chunks/293449b828207656.css +1 -0
  192. package/dist/web/.next/static/chunks/{f55f3c8c1a52f80c.js → 3859477038c381ad.js} +1 -1
  193. package/dist/web/.next/static/chunks/3a5721af09d1c753.js +5 -0
  194. package/dist/web/.next/static/chunks/{8fb2a99c64580de7.js → 98311243e9a5a0ec.js} +1 -1
  195. package/dist/web/.next/static/chunks/{b8e0c1aeea4a14bc.js → a47f36b030917d1f.js} +1 -1
  196. package/dist/web/.next/static/chunks/d60c422421920130.js +5 -0
  197. package/dist/web/.next/static/chunks/{4049cceee6a49323.js → fa78afe3ceed998b.js} +1 -1
  198. package/dist/web/src/app/api/changelog/route.ts +45 -0
  199. package/dist/web/src/app/api/kanban/route.ts +52 -12
  200. package/dist/web/src/app/api/projects/analyze/route.ts +12 -2
  201. package/dist/web/src/app/api/projects/route.ts +1 -11
  202. package/dist/web/src/app/api/providers/route.ts +4 -0
  203. package/dist/web/src/components/ActivityFeed.tsx +3 -0
  204. package/dist/web/src/components/BranchTab.tsx +115 -0
  205. package/dist/web/src/components/ChangelogModal.tsx +234 -0
  206. package/dist/web/src/components/ClaudeTerminalPanel.tsx +124 -70
  207. package/dist/web/src/components/Dashboard.tsx +11 -0
  208. package/dist/web/src/components/GlobalClaudePanel.tsx +6 -0
  209. package/dist/web/src/components/ProjectKanban.tsx +38 -8
  210. package/dist/web/src/components/VersionUpdateToast.tsx +6 -4
  211. package/dist/web/src/components/VoiceControlBar.tsx +1 -1
  212. package/dist/web/src/lib/paths.ts +14 -0
  213. package/dist/web/src/lib/scheduler.ts +2 -1
  214. package/dist/web/src/lib/types.ts +24 -0
  215. package/dist/web/tsconfig.tsbuildinfo +1 -1
  216. package/package.json +1 -1
  217. package/templates/changelog.json +242 -0
  218. package/templates/kanban-seed.json +1 -1
  219. package/templates/store/actions/checkpoint.md +1 -1
  220. package/templates/store/actions/context.md +1 -1
  221. package/templates/store/actions/create-card.md +1 -1
  222. package/templates/store/actions/deep-design.md +13 -3
  223. package/templates/store/actions/design-requirements.md +11 -1
  224. package/templates/store/actions/explore.md +1 -1
  225. package/templates/store/actions/onboard.md +2 -4
  226. package/templates/store/actions/summarize.md +1 -1
  227. package/templates/store/skills/kanban/SKILL.md +77 -3
  228. package/templates/tutorial-project/documentation/kanban.json +1 -1
  229. package/templates/updates/actions/checkpoint.md +1 -1
  230. package/templates/updates/actions/context.md +1 -1
  231. package/templates/updates/actions/create-card.md +1 -1
  232. package/templates/updates/actions/deep-design.md +13 -3
  233. package/templates/updates/actions/design-requirements.md +11 -1
  234. package/templates/updates/actions/explore.md +1 -1
  235. package/templates/updates/actions/onboard.md +2 -4
  236. package/templates/updates/actions/summarize.md +1 -1
  237. package/templates/updates/skills/kanban/SKILL.md +77 -3
  238. package/dist/web/.next/server/chunks/[root-of-the-server]__09aec55a._.js +0 -3
  239. package/dist/web/.next/server/chunks/[root-of-the-server]__12f6cd6f._.js +0 -3
  240. package/dist/web/.next/server/chunks/[root-of-the-server]__15fc9266._.js +0 -18
  241. package/dist/web/.next/server/chunks/[root-of-the-server]__198f01e0._.js +0 -3
  242. package/dist/web/.next/server/chunks/[root-of-the-server]__279e9bf3._.js +0 -3
  243. package/dist/web/.next/server/chunks/[root-of-the-server]__2b639eab._.js +0 -3
  244. package/dist/web/.next/server/chunks/[root-of-the-server]__2d1f0ed9._.js +0 -3
  245. package/dist/web/.next/server/chunks/[root-of-the-server]__3f239285._.js +0 -3
  246. package/dist/web/.next/server/chunks/[root-of-the-server]__47dd878e._.js +0 -3
  247. package/dist/web/.next/server/chunks/[root-of-the-server]__5b8c9374._.js +0 -3
  248. package/dist/web/.next/server/chunks/[root-of-the-server]__5e08b942._.js +0 -3
  249. package/dist/web/.next/server/chunks/[root-of-the-server]__6ffce934._.js +0 -3
  250. package/dist/web/.next/server/chunks/[root-of-the-server]__71bb3374._.js +0 -3
  251. package/dist/web/.next/server/chunks/[root-of-the-server]__7603305e._.js +0 -3
  252. package/dist/web/.next/server/chunks/[root-of-the-server]__7c476ad6._.js +0 -3
  253. package/dist/web/.next/server/chunks/[root-of-the-server]__846ca56f._.js +0 -3
  254. package/dist/web/.next/server/chunks/[root-of-the-server]__98d88050._.js +0 -3
  255. package/dist/web/.next/server/chunks/[root-of-the-server]__b273cc05._.js +0 -3
  256. package/dist/web/.next/server/chunks/[root-of-the-server]__b90bbd70._.js +0 -3
  257. package/dist/web/.next/server/chunks/[root-of-the-server]__d5272169._.js +0 -3
  258. package/dist/web/.next/server/chunks/[root-of-the-server]__d56e68cb._.js +0 -3
  259. package/dist/web/.next/server/chunks/[root-of-the-server]__d6362272._.js +0 -3
  260. package/dist/web/.next/server/chunks/[root-of-the-server]__de1277ee._.js +0 -27
  261. package/dist/web/.next/server/chunks/[root-of-the-server]__e88a19d2._.js +0 -3
  262. package/dist/web/.next/server/chunks/[root-of-the-server]__f3e501b6._.js +0 -3
  263. package/dist/web/.next/server/chunks/[root-of-the-server]__f59af2bc._.js +0 -3
  264. package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__9ac6ea25._.js +0 -3
  265. package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__dfe2728c._.js +0 -3
  266. package/dist/web/.next/static/chunks/3d5195b57fc05540.js +0 -4
  267. package/dist/web/.next/static/chunks/59fb302a5bfd2dc0.js +0 -4
  268. package/dist/web/.next/static/chunks/747f5e5f9dcf2621.css +0 -1
  269. /package/dist/web/.next/static/{0sPAbk-Qw-InZ0rdHjHnC → aN-jqftQVvSm0qVskLybH}/_buildManifest.js +0 -0
  270. /package/dist/web/.next/static/{0sPAbk-Qw-InZ0rdHjHnC → aN-jqftQVvSm0qVskLybH}/_clientMiddlewareManifest.json +0 -0
  271. /package/dist/web/.next/static/{0sPAbk-Qw-InZ0rdHjHnC → aN-jqftQVvSm0qVskLybH}/_ssgManifest.js +0 -0
@@ -13,11 +13,16 @@ interface ProviderConfig {
13
13
  command: string;
14
14
  permissions: { flag: string; label: string; default: boolean };
15
15
  resume: { supported: boolean };
16
+ model?: {
17
+ flag: string;
18
+ available: Array<{ id: string; label: string; description?: string }>;
19
+ };
16
20
  }
17
21
 
18
22
  interface ProviderDefault {
19
23
  provider: string;
20
24
  skipPermissions: boolean;
25
+ model?: string;
21
26
  }
22
27
 
23
28
  interface ProvidersData {
@@ -45,6 +50,7 @@ interface SessionInfo {
45
50
  claudeSessionId?: string | null;
46
51
  provider?: string;
47
52
  skipPermissions?: boolean;
53
+ model?: string;
48
54
  }
49
55
 
50
56
  export interface TerminalContext {
@@ -145,6 +151,13 @@ export function ClaudeTerminalPanel({
145
151
  // Spawn error toast — shown when session creation fails (e.g. posix_spawnp failed)
146
152
  const [spawnError, setSpawnError] = useState<string | null>(null);
147
153
 
154
+ // Model selection state
155
+ const [selectedModel, setSelectedModel] = useState(''); // '' = Default (no flag)
156
+ const prevProviderRef = useRef(selectedProvider);
157
+ const userSelectedModelRef = useRef(false);
158
+ const sessionInfoRef = useRef(sessionInfo);
159
+ sessionInfoRef.current = sessionInfo;
160
+
148
161
  // Instruction file check state
149
162
  const [instructionFileCheck, setInstructionFileCheck] = useState<{ needed: boolean; targetFile?: string; copySource?: string } | null>(null);
150
163
  const [createInstructionFile, setCreateInstructionFile] = useState(true);
@@ -177,15 +190,53 @@ export function ClaudeTerminalPanel({
177
190
  setSelectedProvider(def.provider);
178
191
  }
179
192
  setSkipPermissions(def.skipPermissions);
193
+ if (def.model) {
194
+ setSelectedModel(def.model);
195
+ }
180
196
  }
181
197
  }
182
198
  })
183
199
  .catch(() => { /* providers.json not available, use defaults */ });
184
200
  }, [stage]);
185
201
 
202
+ // Pre-fill model when provider changes
203
+ useEffect(() => {
204
+ if (!providersData) return;
205
+ const providerChanged = prevProviderRef.current !== selectedProvider;
206
+ prevProviderRef.current = selectedProvider;
207
+ if (!providerChanged && userSelectedModelRef.current) return;
208
+ userSelectedModelRef.current = false;
209
+ // Priority: last-used from session > stage default > Default
210
+ const si = sessionInfoRef.current;
211
+ if (si?.model && si.provider === selectedProvider) {
212
+ const available = providersData.providers[selectedProvider]?.model?.available;
213
+ if (available?.some(m => m.id === si.model)) {
214
+ setSelectedModel(si.model);
215
+ return;
216
+ }
217
+ }
218
+ const stageDefault = stage ? providersData.defaults.stages[stage] : null;
219
+ const def = stageDefault || providersData.defaults.global;
220
+ if (def?.model && def.provider === selectedProvider) {
221
+ const available = providersData.providers[selectedProvider]?.model?.available;
222
+ if (available?.some(m => m.id === def.model)) {
223
+ setSelectedModel(def.model);
224
+ return;
225
+ }
226
+ }
227
+ setSelectedModel('');
228
+ }, [selectedProvider, providersData, stage]);
229
+
186
230
  // Persist provider default to /api/providers (fire-and-forget)
187
231
  const saveProviderDefault = useCallback((provider: string, skip: boolean) => {
188
- const defaultVal = { provider, skipPermissions: skip };
232
+ // Preserve any existing model field in the stage default (stage model defaults are intentional config)
233
+ const existingDefault = stage
234
+ ? providersData?.defaults.stages[stage]
235
+ : providersData?.defaults.global;
236
+ const defaultVal: Record<string, unknown> = { provider, skipPermissions: skip };
237
+ if (existingDefault?.model && existingDefault.provider === provider) {
238
+ defaultVal.model = existingDefault.model;
239
+ }
189
240
  const defaults = stage
190
241
  ? { stages: { [stage]: defaultVal } }
191
242
  : { global: defaultVal };
@@ -194,7 +245,7 @@ export function ClaudeTerminalPanel({
194
245
  headers: { 'Content-Type': 'application/json' },
195
246
  body: JSON.stringify({ defaults }),
196
247
  }).catch(() => { /* preference save — ignore errors */ });
197
- }, [stage]);
248
+ }, [stage, providersData]);
198
249
 
199
250
  const isRunning = sessionInfo?.status === 'running' || sessionInfo?.status === 'detached';
200
251
  const hasHistory = sessionInfo?.hasHistory;
@@ -288,6 +339,74 @@ export function ClaudeTerminalPanel({
288
339
  </div>
289
340
  ) : null;
290
341
 
342
+ // Helper to render model label from provider config
343
+ const getModelLabel = (providerId: string, modelId?: string) => {
344
+ if (!modelId || !providersData) return null;
345
+ return providersData.providers[providerId]?.model?.available?.find(m => m.id === modelId)?.label || modelId;
346
+ };
347
+
348
+ // Shared provider selector (eliminates duplication between resume and fresh-start screens)
349
+ const renderProviderSelector = (options?: { showModel?: boolean }) => {
350
+ if (!providersData || Object.keys(providersData.providers).length <= 1) return null;
351
+ const currentProvider = providersData.providers[selectedProvider];
352
+ const models = currentProvider?.model?.available;
353
+ const showModel = options?.showModel !== false;
354
+ return (
355
+ <div className="flex flex-col items-center gap-2 mt-3 pt-3 border-t border-void-700/50">
356
+ <div className="flex gap-1">
357
+ {Object.values(providersData.providers).map(p => (
358
+ <button
359
+ key={p.id}
360
+ onClick={() => {
361
+ setSelectedProvider(p.id);
362
+ saveProviderDefault(p.id, skipPermissions);
363
+ onProviderChange?.(p.id);
364
+ }}
365
+ className={`rounded-md px-3 py-1 text-xs font-medium transition-all ${
366
+ selectedProvider === p.id
367
+ ? 'border border-neon-blue-400/60 bg-neon-blue-400/15 text-neon-blue-400 shadow-[0_0_8px_rgba(0,191,255,0.2)]'
368
+ : 'border border-void-600 bg-void-800 text-void-400 hover:border-void-500 hover:text-void-300'
369
+ }`}
370
+ >
371
+ {p.displayName}
372
+ </button>
373
+ ))}
374
+ </div>
375
+ <div className="flex items-center gap-3">
376
+ {showModel && models && models.length > 0 && (
377
+ <div className="flex items-center gap-1.5">
378
+ <span className="text-xs text-void-500">Model</span>
379
+ <select
380
+ value={selectedModel}
381
+ onChange={(e) => { setSelectedModel(e.target.value); userSelectedModelRef.current = true; }}
382
+ className={`max-w-[140px] truncate rounded border px-2 py-1 text-xs font-medium transition-all ${
383
+ selectedModel
384
+ ? 'border-neon-blue-400/40 bg-neon-blue-400/10 text-neon-blue-400'
385
+ : 'border-void-600 bg-void-800 text-void-400'
386
+ }`}
387
+ >
388
+ <option value="">Default</option>
389
+ {models.map(m => (
390
+ <option key={m.id} value={m.id}>{m.label}</option>
391
+ ))}
392
+ </select>
393
+ </div>
394
+ )}
395
+ <label className="flex items-center gap-1.5 text-xs text-void-500 cursor-pointer">
396
+ <input
397
+ type="checkbox"
398
+ checked={skipPermissions}
399
+ onChange={(e) => { setSkipPermissions(e.target.checked); saveProviderDefault(selectedProvider, e.target.checked); }}
400
+ className="rounded border-void-600"
401
+ />
402
+ {providersData.providers[selectedProvider]?.permissions.label || 'Skip permissions'}
403
+ </label>
404
+ </div>
405
+ {instructionFileWarning}
406
+ </div>
407
+ );
408
+ };
409
+
291
410
  // Derive startup and toolbar lists from placement
292
411
  const startupActions = actions.filter(a => a.placement === 'startup' || a.placement === 'both');
293
412
  const toolbarActions = actions.filter(a => a.placement === 'toolbar' || a.placement === 'both');
@@ -317,6 +436,7 @@ export function ClaudeTerminalPanel({
317
436
  cwd,
318
437
  fresh: !hasHistory,
319
438
  prompt,
439
+ model: selectedModel || undefined,
320
440
  createInstructionFile: instructionFileCheck?.needed ? createInstructionFile : undefined,
321
441
  }),
322
442
  });
@@ -619,40 +739,7 @@ export function ClaudeTerminalPanel({
619
739
  Custom...
620
740
  </button>
621
741
  </div>
622
- {/* Provider selector */}
623
- {providersData && Object.keys(providersData.providers).length > 1 && (
624
- <div className="flex flex-col items-center gap-2 mt-3 pt-3 border-t border-void-700/50">
625
- <div className="flex gap-1">
626
- {Object.values(providersData.providers).map(p => (
627
- <button
628
- key={p.id}
629
- onClick={() => {
630
- setSelectedProvider(p.id);
631
- saveProviderDefault(p.id, skipPermissions);
632
- onProviderChange?.(p.id);
633
- }}
634
- className={`rounded-md px-3 py-1 text-xs font-medium transition-all ${
635
- selectedProvider === p.id
636
- ? 'border border-neon-blue-400/60 bg-neon-blue-400/15 text-neon-blue-400 shadow-[0_0_8px_rgba(0,191,255,0.2)]'
637
- : 'border border-void-600 bg-void-800 text-void-400 hover:border-void-500 hover:text-void-300'
638
- }`}
639
- >
640
- {p.displayName}
641
- </button>
642
- ))}
643
- </div>
644
- <label className="flex items-center gap-1.5 text-xs text-void-500 cursor-pointer">
645
- <input
646
- type="checkbox"
647
- checked={skipPermissions}
648
- onChange={(e) => { setSkipPermissions(e.target.checked); saveProviderDefault(selectedProvider, e.target.checked); }}
649
- className="rounded border-void-600"
650
- />
651
- {providersData.providers[selectedProvider]?.permissions.label || 'Skip permissions'}
652
- </label>
653
- {instructionFileWarning}
654
- </div>
655
- )}
742
+ {renderProviderSelector({ showModel: false })}
656
743
  </>
657
744
  ) : (
658
745
  <>
@@ -684,40 +771,7 @@ export function ClaudeTerminalPanel({
684
771
  >
685
772
  Start without prompt
686
773
  </button>
687
- {/* Provider selector */}
688
- {providersData && Object.keys(providersData.providers).length > 1 && (
689
- <div className="flex flex-col items-center gap-2 mt-3 pt-3 border-t border-void-700/50">
690
- <div className="flex gap-1">
691
- {Object.values(providersData.providers).map(p => (
692
- <button
693
- key={p.id}
694
- onClick={() => {
695
- setSelectedProvider(p.id);
696
- saveProviderDefault(p.id, skipPermissions);
697
- onProviderChange?.(p.id);
698
- }}
699
- className={`rounded-md px-3 py-1 text-xs font-medium transition-all ${
700
- selectedProvider === p.id
701
- ? 'border border-neon-blue-400/60 bg-neon-blue-400/15 text-neon-blue-400 shadow-[0_0_8px_rgba(0,191,255,0.2)]'
702
- : 'border border-void-600 bg-void-800 text-void-400 hover:border-void-500 hover:text-void-300'
703
- }`}
704
- >
705
- {p.displayName}
706
- </button>
707
- ))}
708
- </div>
709
- <label className="flex items-center gap-1.5 text-xs text-void-500 cursor-pointer">
710
- <input
711
- type="checkbox"
712
- checked={skipPermissions}
713
- onChange={(e) => { setSkipPermissions(e.target.checked); saveProviderDefault(selectedProvider, e.target.checked); }}
714
- className="rounded border-void-600"
715
- />
716
- {providersData.providers[selectedProvider]?.permissions.label || 'Skip permissions'}
717
- </label>
718
- {instructionFileWarning}
719
- </div>
720
- )}
774
+ {renderProviderSelector()}
721
775
  </>
722
776
  )}
723
777
  </div>
@@ -15,6 +15,7 @@ import { CliAssetsTab } from './CliAssetsTab';
15
15
  import { ActivityFeed } from './ActivityFeed';
16
16
  import { ThemeToggle } from './ThemeToggle';
17
17
  import { VersionUpdateToast } from './VersionUpdateToast';
18
+ import { ChangelogModal } from './ChangelogModal';
18
19
  import { useVoice } from '@/contexts/VoiceContext';
19
20
 
20
21
  interface DashboardProps {
@@ -35,6 +36,7 @@ export function Dashboard({ data: initialData }: DashboardProps) {
35
36
  const [dropIndex, setDropIndex] = useState<number | null>(null);
36
37
  const gridRef = useRef<HTMLDivElement>(null);
37
38
  const [slycodeVersion, setSlycodeVersion] = useState<string | null>(null);
39
+ const [showChangelog, setShowChangelog] = useState(false);
38
40
 
39
41
  // Fetch SlyCode version on mount
40
42
  useEffect(() => {
@@ -472,6 +474,13 @@ export function Dashboard({ data: initialData }: DashboardProps) {
472
474
  <p className="mt-2 text-xs text-void-500 dark:text-void-400">
473
475
  &copy; 2026 SlyCode (<a href="https://slycode.ai" target="_blank" rel="noopener noreferrer" className="hover:text-neon-blue-500 dark:hover:text-neon-blue-400 transition-colors">slycode.ai</a>). All rights reserved.
474
476
  {slycodeVersion && <span className="ml-2 text-void-400 dark:text-void-500">v{slycodeVersion}</span>}
477
+ <button
478
+ type="button"
479
+ onClick={() => setShowChangelog(true)}
480
+ className="ml-2 text-void-400 hover:text-neon-blue-500 dark:text-void-500 dark:hover:text-neon-blue-400 transition-colors underline-offset-2 hover:underline"
481
+ >
482
+ Changelog
483
+ </button>
475
484
  </p>
476
485
  </footer>
477
486
  </main>
@@ -482,6 +491,8 @@ export function Dashboard({ data: initialData }: DashboardProps) {
482
491
  onCreated={refreshData}
483
492
  />
484
493
 
494
+ {showChangelog && <ChangelogModal onClose={() => setShowChangelog(false)} />}
495
+
485
496
  {/* Global Terminal */}
486
497
  <GlobalClaudePanel
487
498
  sessionNameOverride="global:global"
@@ -8,6 +8,7 @@ import {
8
8
  import { useSlyActionsConfig } from '@/hooks/useSlyActionsConfig';
9
9
  import { onTerminalPrompt } from '@/lib/terminal-events';
10
10
  import { ClaudeTerminalPanel, type TerminalContext } from './ClaudeTerminalPanel';
11
+ import { BranchTab } from './BranchTab';
11
12
  import { useVoice } from '@/contexts/VoiceContext';
12
13
 
13
14
  interface SessionInfo {
@@ -264,6 +265,11 @@ export function GlobalClaudePanel({
264
265
  />
265
266
  </div>
266
267
  )}
268
+
269
+ {/* Git branch tab - positioned to the left of the panel */}
270
+ {cwd && (
271
+ <BranchTab projectPath={cwd} isTerminalExpanded={isExpanded} />
272
+ )}
267
273
  </div>
268
274
  );
269
275
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
4
4
  import { useSearchParams, useRouter, usePathname } from 'next/navigation';
5
- import type { ProjectWithBacklog, KanbanCard, KanbanStage, KanbanStages, BridgeStats, Priority } from '@/lib/types';
5
+ import type { ProjectWithBacklog, KanbanCard, KanbanStage, KanbanStages, BridgeStats, Priority, ChangedCard, CardChangeType } from '@/lib/types';
6
6
  import { connectionManager } from '@/lib/connection-manager';
7
7
  import { tabSync } from '@/lib/tab-sync';
8
8
  import { usePolling } from '@/hooks/usePolling';
@@ -85,6 +85,8 @@ export function ProjectKanban({ project, projectPath, showArchived = false, show
85
85
  const [activeCards, setActiveCards] = useState<Set<string>>(new Set());
86
86
  const prevActiveCardsRef = useRef<Set<string>>(new Set());
87
87
  const consumedCardParamRef = useRef<string | null>(null);
88
+ const editedCardIdsRef = useRef<Set<string>>(new Set());
89
+ const movedCardIdsRef = useRef<Set<string>>(new Set());
88
90
 
89
91
  // Context menu state
90
92
  const [contextMenu, setContextMenu] = useState<{ position: { x: number; y: number }; card: KanbanCard; stage: KanbanStage } | null>(null);
@@ -376,8 +378,8 @@ export function ProjectKanban({ project, projectPath, showArchived = false, show
376
378
  setSaveStatus('saving');
377
379
  isSavingRef.current = true;
378
380
  try {
379
- // Compute which cards actually changed vs the clean baseline
380
- const changedCardIds: string[] = [];
381
+ // Compute which cards actually changed vs the clean baseline, with typed changeset
382
+ const changedCards: ChangedCard[] = [];
381
383
  const baselineCards = new Map<string, string>();
382
384
  for (const stage of STAGE_ORDER) {
383
385
  for (const card of cleanBaselineRef.current[stage] || []) {
@@ -389,29 +391,51 @@ export function ProjectKanban({ project, projectPath, showArchived = false, show
389
391
  const baselineKey = baselineCards.get(card.id);
390
392
  const currentKey = JSON.stringify(card) + '|' + stage;
391
393
  if (baselineKey !== currentKey) {
392
- changedCardIds.push(card.id); // New, modified, or moved
394
+ if (editedCardIdsRef.current.has(card.id)) {
395
+ changedCards.push({ id: card.id, type: 'edit' }); // Edit wins over move
396
+ } else if (movedCardIdsRef.current.has(card.id)) {
397
+ changedCards.push({ id: card.id, type: 'move' });
398
+ }
399
+ // Untracked divergence (SSE timing, connection issues, normalizeOrder drift)
400
+ // — skip it. The server merge preserves disk truth for cards not in changedCards.
393
401
  }
394
402
  baselineCards.delete(card.id);
395
403
  }
396
404
  }
397
405
  // Cards in baseline but not in current = deleted
398
406
  for (const deletedId of baselineCards.keys()) {
399
- changedCardIds.push(deletedId);
407
+ changedCards.push({ id: deletedId, type: 'delete' });
400
408
  }
401
409
 
410
+ // Clear tracking refs after building payload
411
+ editedCardIdsRef.current.clear();
412
+ movedCardIdsRef.current.clear();
413
+
402
414
  const res = await fetch('/api/kanban', {
403
415
  method: 'POST',
404
416
  headers: { 'Content-Type': 'application/json' },
405
- body: JSON.stringify({ projectId: project.id, stages: stagesToSave, changedCardIds }),
417
+ body: JSON.stringify({ projectId: project.id, stages: stagesToSave, changedCards }),
406
418
  });
407
419
  if (res.ok) {
408
420
  const data = await res.json();
409
421
  lastSaveTimestampRef.current = data.last_updated;
410
- cleanBaselineRef.current = stagesToSave;
422
+ // Use server's merged+normalized stages as baseline (eliminates normalizeOrder drift)
423
+ const serverStages = data.stages;
424
+ if (serverStages) {
425
+ cleanBaselineRef.current = serverStages;
426
+ // Sync React state with server truth if no new user edits happened during save.
427
+ // Without this, baseline and stages diverge — cards the user never touched appear
428
+ // "changed" in the next save cycle and get reverted to stale frontend positions.
429
+ if (stagesEqual(stagesRef.current, stagesToSave)) {
430
+ setStages(serverStages);
431
+ }
432
+ } else {
433
+ cleanBaselineRef.current = stagesToSave;
434
+ }
411
435
  // Only clear dirty if stages haven't changed during the save.
412
436
  // If the user made edits while the save was in flight, stay dirty
413
437
  // so SSE/polling won't overwrite their pending changes.
414
- isDirtyRef.current = !stagesEqual(stagesRef.current, stagesToSave);
438
+ isDirtyRef.current = !stagesEqual(stagesRef.current, cleanBaselineRef.current);
415
439
  setSaveStatus('saved');
416
440
  // Notify other tabs immediately
417
441
  tabSync.broadcast('kanban-update', project.id);
@@ -544,6 +568,8 @@ export function ProjectKanban({ project, projectPath, showArchived = false, show
544
568
 
545
569
  if (!selectedStage) return;
546
570
 
571
+ editedCardIdsRef.current.add(updatedCard.id);
572
+
547
573
  // Update stages - selectedCard will be automatically derived from the updated stages
548
574
  setStages((prev) => ({
549
575
  ...prev,
@@ -577,6 +603,7 @@ export function ProjectKanban({ project, projectPath, showArchived = false, show
577
603
  };
578
604
 
579
605
  const handleMoveCard = (cardId: string, newStage: KanbanStage, insertIndex?: number) => {
606
+ movedCardIdsRef.current.add(cardId);
580
607
  setStages((prev) => {
581
608
  // Find which stage the card is currently in
582
609
  let sourceStage: KanbanStage | null = null;
@@ -695,6 +722,7 @@ export function ProjectKanban({ project, projectPath, showArchived = false, show
695
722
  checked: p === card.priority,
696
723
  disabled: p === card.priority,
697
724
  onClick: () => {
725
+ editedCardIdsRef.current.add(card.id);
698
726
  setStages((prev) => ({
699
727
  ...prev,
700
728
  [stage]: prev[stage].map((c) =>
@@ -731,6 +759,7 @@ export function ProjectKanban({ project, projectPath, showArchived = false, show
731
759
  disabled: !!card.automation,
732
760
  onClick: () => {
733
761
  if (card.automation) return;
762
+ editedCardIdsRef.current.add(card.id);
734
763
  setStages((prev) => ({
735
764
  ...prev,
736
765
  [stage]: prev[stage].map((c) =>
@@ -767,6 +796,7 @@ export function ProjectKanban({ project, projectPath, showArchived = false, show
767
796
  checked: p === card.priority,
768
797
  disabled: p === card.priority,
769
798
  onClick: () => {
799
+ editedCardIdsRef.current.add(card.id);
770
800
  setStages((prev) => ({
771
801
  ...prev,
772
802
  [stage]: prev[stage].map((c) =>
@@ -53,12 +53,14 @@ export function VersionUpdateToast() {
53
53
  />
54
54
  <span className="text-sm text-void-700 dark:text-void-300">
55
55
  SlyCode <span className="font-medium text-neon-blue-500 dark:text-neon-blue-400">v{info.latest}</span> available
56
- <span className="text-void-500 dark:text-void-400"> (current: v{info.current})</span>
57
56
  </span>
58
57
  </div>
59
- <code className="rounded bg-void-100 px-1.5 py-0.5 text-xs text-void-600 dark:bg-void-800 dark:text-void-400">
60
- slycode update
61
- </code>
58
+ <div className="flex items-center gap-1.5">
59
+ <span className="text-xs text-void-500 dark:text-void-400">run in terminal:</span>
60
+ <code className="rounded bg-void-100 px-1.5 py-0.5 text-xs text-void-600 dark:bg-void-800 dark:text-void-400">
61
+ slycode update
62
+ </code>
63
+ </div>
62
64
  <button
63
65
  onClick={handleDismiss}
64
66
  className="ml-1 rounded p-0.5 text-void-400 transition-colors hover:bg-void-200 hover:text-void-600 dark:text-void-500 dark:hover:bg-void-800 dark:hover:text-void-300"
@@ -45,7 +45,7 @@ export function VoiceControlBar({
45
45
  onClick={onRecord}
46
46
  disabled={disabled}
47
47
  className="rounded-md border border-void-400/30 bg-void-200/50 p-1.5 text-void-500 transition-all hover:border-red-400/40 hover:bg-red-400/10 hover:text-red-400 disabled:opacity-50 dark:border-void-500/25 dark:bg-void-700/50 dark:text-void-400 dark:hover:border-red-400/40 dark:hover:bg-red-400/10 dark:hover:text-red-400"
48
- title="Start recording (Ctrl+.)"
48
+ title="Start recording [Ctrl+.]"
49
49
  >
50
50
  <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
51
51
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" />
@@ -7,6 +7,20 @@
7
7
 
8
8
  import path from 'path';
9
9
  import fs from 'fs';
10
+ import os from 'os';
11
+
12
+ /**
13
+ * Expand tilde (~) in a path to the user's home directory.
14
+ * Also normalizes Unicode tildes (U+02DC, U+FF5E) that Mac browsers can produce.
15
+ */
16
+ export function expandTilde(p: string): string {
17
+ // Normalize Unicode tildes (U+02DC small tilde, U+FF5E fullwidth tilde) to ASCII
18
+ p = p.replace(/^[\u02dc\uff5e]/, '~');
19
+ if (p.startsWith('~/') || p === '~') {
20
+ return p.replace(/^~/, os.homedir());
21
+ }
22
+ return p;
23
+ }
10
24
 
11
25
  /**
12
26
  * Resolve the SlyCode root directory (workspace).
@@ -666,7 +666,8 @@ async function checkAutomations(): Promise<void> {
666
666
  result.error.includes('Session create failed') ||
667
667
  result.error.includes('Session stopped') ||
668
668
  result.error.includes('Input failed') ||
669
- result.error.includes('No automation config')
669
+ result.error.includes('No automation config') ||
670
+ result.error.includes('No activity detected')
670
671
  );
671
672
  if (isHardFailure) {
672
673
  await sendErrorNotification(card.title, result.error || 'Unknown error', result.sessionName);
@@ -128,6 +128,13 @@ export interface KanbanStages {
128
128
  done: KanbanCard[];
129
129
  }
130
130
 
131
+ export type CardChangeType = 'move' | 'edit' | 'create' | 'delete';
132
+
133
+ export interface ChangedCard {
134
+ id: string;
135
+ type: CardChangeType;
136
+ }
137
+
131
138
  export interface KanbanBoard {
132
139
  project_id: string;
133
140
  stages: KanbanStages;
@@ -163,6 +170,23 @@ export interface FeatureEntry {
163
170
  projectId?: string;
164
171
  }
165
172
 
173
+ // ============================================================================
174
+ // Changelog Types
175
+ // ============================================================================
176
+
177
+ export type ChangelogChangeType = 'feature' | 'bugfix' | 'improvement' | 'chore';
178
+
179
+ export interface ChangelogChange {
180
+ type: ChangelogChangeType;
181
+ description: string;
182
+ }
183
+
184
+ export interface ChangelogVersion {
185
+ version: string;
186
+ date: string; // YYYY-MM-DD
187
+ changes: ChangelogChange[];
188
+ }
189
+
166
190
  // ============================================================================
167
191
  // Questionnaire Types (per design doc section 3.3)
168
192
  // ============================================================================