@webship.co/workspace 1.0.0-alpha1 → 1.0.0-beta2

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 (634) hide show
  1. package/LICENSE +339 -0
  2. package/README.md +28 -364
  3. package/core/bin/workspace.js +135 -0
  4. package/core/config/settings.labels.yml +514 -0
  5. package/core/config/settings.template.yml +292 -0
  6. package/core/config/style.yml +53 -0
  7. package/core/config/theme.default.yml +161 -0
  8. package/core/config/theme.dispatch.yml +166 -0
  9. package/core/config/theme.drupal.yml +175 -0
  10. package/core/config/theme.ember.yml +160 -0
  11. package/core/config/theme.github.yml +172 -0
  12. package/core/config/theme.gitlab.yml +168 -0
  13. package/core/config/theme.press.yml +161 -0
  14. package/core/config/theme.slate.yml +160 -0
  15. package/core/config/theme.upsun.yml +174 -0
  16. package/core/docs/architecture.svg +65 -0
  17. package/core/docs/getting-started.md +51 -0
  18. package/core/docs/plugins.md +27 -0
  19. package/core/docs/releases.md +23 -0
  20. package/core/docs/testing.md +15 -0
  21. package/core/docs/workspaces.md +25 -0
  22. package/core/scripts/args/arg-ai-items.sh +65 -0
  23. package/core/scripts/args/arg-autostart.sh +31 -0
  24. package/core/scripts/args/arg-avatar-agent.sh +28 -0
  25. package/core/scripts/args/arg-avatar-catalog.sh +12 -0
  26. package/core/scripts/args/arg-avatar-character.sh +68 -0
  27. package/core/scripts/args/arg-avatar-voice.sh +24 -0
  28. package/core/scripts/args/arg-avatar.sh +22 -0
  29. package/core/scripts/args/arg-checks.sh +36 -0
  30. package/core/scripts/args/arg-clone.sh +42 -0
  31. package/core/scripts/args/arg-commands.sh +51 -0
  32. package/core/scripts/args/arg-cucumber.sh +34 -0
  33. package/core/scripts/args/arg-distribution.sh +30 -0
  34. package/core/scripts/args/arg-drupal.sh +39 -0
  35. package/core/scripts/args/arg-drupal_cms.sh +18 -0
  36. package/core/scripts/args/arg-gitlab-ci-local.sh +25 -0
  37. package/core/scripts/args/arg-graphify.sh +36 -0
  38. package/core/scripts/args/arg-graphs.sh +12 -0
  39. package/core/scripts/args/arg-hostnames.sh +23 -0
  40. package/core/scripts/args/arg-milvus-backup.sh +8 -0
  41. package/core/scripts/args/arg-milvus-mcp.sh +12 -0
  42. package/core/scripts/args/arg-milvus.sh +32 -0
  43. package/core/scripts/args/arg-obsidian.sh +17 -0
  44. package/core/scripts/args/arg-odoo.sh +18 -0
  45. package/core/scripts/args/arg-penpot-backup.sh +8 -0
  46. package/core/scripts/args/arg-penpot-mcp.sh +15 -0
  47. package/core/scripts/args/arg-penpot-token.sh +17 -0
  48. package/core/scripts/args/arg-penpot.sh +44 -0
  49. package/core/scripts/args/arg-rag.sh +21 -0
  50. package/core/scripts/args/arg-ragify.sh +35 -0
  51. package/core/scripts/args/arg-reclaim.sh +18 -0
  52. package/core/scripts/args/arg-release.sh +21 -0
  53. package/core/scripts/args/arg-screen.sh +28 -0
  54. package/core/scripts/args/arg-site-template.sh +13 -0
  55. package/core/scripts/args/arg-spdd.sh +16 -0
  56. package/core/scripts/args/arg-specs.sh +22 -0
  57. package/core/scripts/args/arg-storage.sh +36 -0
  58. package/core/scripts/args/arg-testing.sh +36 -0
  59. package/core/scripts/args/arg-webship.sh +34 -0
  60. package/core/scripts/args/arg-webships-project.sh +22 -0
  61. package/core/scripts/args/arg-webships-site-template.sh +17 -0
  62. package/core/scripts/args/arg-webships.sh +34 -0
  63. package/core/scripts/args/arg-website-site-template.sh +17 -0
  64. package/core/scripts/args/arg-website.sh +25 -0
  65. package/core/scripts/args/arg-wordpress.sh +18 -0
  66. package/core/scripts/autostart-run.sh +162 -0
  67. package/core/scripts/autostart-stop.sh +30 -0
  68. package/core/scripts/autostart-watch.sh +43 -0
  69. package/core/scripts/autostart.sh +26 -0
  70. package/core/scripts/bootstrap-functions.sh +117 -0
  71. package/core/scripts/bootstrap-libraries.sh +10 -0
  72. package/core/scripts/bootstrap.sh +71 -0
  73. package/core/scripts/ddev-shim/ddev +122 -0
  74. package/core/scripts/docker-gid.sh +119 -0
  75. package/core/scripts/functions/arg-site-template.sh +28 -0
  76. package/core/scripts/functions/fun-ai-items-lock.sh +175 -0
  77. package/core/scripts/functions/fun-ai-items-sources.sh +298 -0
  78. package/core/scripts/functions/fun-ai-items.sh +621 -0
  79. package/core/scripts/functions/fun-ai-providers.sh +72 -0
  80. package/core/scripts/functions/fun-autostart.sh +429 -0
  81. package/core/scripts/functions/fun-avatar.sh +441 -0
  82. package/core/scripts/functions/fun-backup-item.sh +50 -0
  83. package/core/scripts/functions/fun-build-distribution.sh +226 -0
  84. package/core/scripts/functions/fun-build-site-template.sh +104 -0
  85. package/core/scripts/functions/fun-build-webships.sh +161 -0
  86. package/core/scripts/functions/fun-build-website.sh +199 -0
  87. package/core/scripts/functions/fun-checks.sh +243 -0
  88. package/core/scripts/functions/fun-clone-project.sh +289 -0
  89. package/core/scripts/functions/fun-commands.sh +521 -0
  90. package/core/scripts/functions/fun-database.sh +63 -0
  91. package/core/scripts/functions/fun-ddev-hostnames.sh +213 -0
  92. package/core/scripts/functions/fun-ddev-nodejs.sh +15 -0
  93. package/core/scripts/functions/fun-ddev-telemetry.sh +15 -0
  94. package/core/scripts/functions/fun-default-settings.sh +28 -0
  95. package/core/scripts/functions/fun-distribution-cucumber.sh +45 -0
  96. package/core/scripts/functions/fun-distribution-drupal.sh +25 -0
  97. package/core/scripts/functions/fun-distribution-drupal_cms.sh +35 -0
  98. package/core/scripts/functions/fun-distribution-webship.sh +28 -0
  99. package/core/scripts/functions/fun-distribution-webships.sh +33 -0
  100. package/core/scripts/functions/fun-distribution-website.sh +19 -0
  101. package/core/scripts/functions/fun-drush.sh +48 -0
  102. package/core/scripts/functions/fun-elevenlabs.sh +179 -0
  103. package/core/scripts/functions/fun-files.sh +19 -0
  104. package/core/scripts/functions/fun-git-worktrees.sh +142 -0
  105. package/core/scripts/functions/fun-gitlab-ci.sh +156 -0
  106. package/core/scripts/functions/fun-hostnames-cli.sh +88 -0
  107. package/core/scripts/functions/fun-milvus.sh +584 -0
  108. package/core/scripts/functions/fun-odoo.sh +121 -0
  109. package/core/scripts/functions/fun-penpot.sh +724 -0
  110. package/core/scripts/functions/fun-project-name.sh +20 -0
  111. package/core/scripts/functions/fun-rag.sh +253 -0
  112. package/core/scripts/functions/fun-ragify.sh +165 -0
  113. package/core/scripts/functions/fun-reclaim.sh +297 -0
  114. package/core/scripts/functions/fun-release.sh +159 -0
  115. package/core/scripts/functions/fun-remove-project.sh +319 -0
  116. package/core/scripts/functions/fun-screen.sh +96 -0
  117. package/core/scripts/functions/fun-spdd.sh +676 -0
  118. package/core/scripts/functions/fun-specs-share.sh +237 -0
  119. package/core/scripts/functions/fun-storage.sh +347 -0
  120. package/core/scripts/functions/fun-testing.sh +329 -0
  121. package/core/scripts/functions/fun-users.sh +87 -0
  122. package/core/scripts/functions/fun-version.sh +72 -0
  123. package/core/scripts/functions/fun-video-edit.sh +48 -0
  124. package/core/scripts/functions/fun-voice.sh +45 -0
  125. package/core/scripts/functions/fun-wordpress.sh +59 -0
  126. package/core/scripts/host-mounts.sh +161 -0
  127. package/core/scripts/install.sh +602 -0
  128. package/core/scripts/libs/__pycache__/video_edit.cpython-312.pyc +0 -0
  129. package/core/scripts/libs/argparse.sh +93 -0
  130. package/core/scripts/libs/bash-yaml.sh +52 -0
  131. package/core/scripts/libs/checks_plan.js +312 -0
  132. package/core/scripts/libs/elevenlabs.py +627 -0
  133. package/core/scripts/libs/progress-bar.sh +19 -0
  134. package/core/scripts/libs/rag_index.py +446 -0
  135. package/core/scripts/libs/video_edit.py +636 -0
  136. package/core/scripts/libs/voice.py +430 -0
  137. package/core/scripts/plugins/jira/host-install.sh +119 -0
  138. package/core/scripts/plugins/jira/host-mounts.txt +2 -0
  139. package/core/scripts/plugins/upsun/args/arg-upsun.sh +51 -0
  140. package/core/scripts/plugins/upsun/functions/fun-upsun.sh +304 -0
  141. package/core/scripts/plugins/upsun/host-install.sh +99 -0
  142. package/core/scripts/plugins/upsun/host-mounts.txt +1 -0
  143. package/core/scripts/plugins/upsun/upsun-ai.sh +133 -0
  144. package/core/scripts/plugins.sh +152 -0
  145. package/core/scripts/release.sh +69 -0
  146. package/core/scripts/storage.sh +39 -0
  147. package/core/scripts/sync-tooling.sh +246 -0
  148. package/core/scripts/tests/cmd-smoke-test.sh +109 -0
  149. package/core/scripts/trust-https.sh +257 -0
  150. package/core/workspace-app/.ddev/commands/web/test-dashboard +49 -0
  151. package/core/workspace-app/.ddev/config.yaml +400 -0
  152. package/core/workspace-app/.ddev/docker-compose.dood.yaml +45 -0
  153. package/core/workspace-app/.ddev/docker-compose.plugin-jira-twg.yaml +26 -0
  154. package/core/workspace-app/.ddev/docker-compose.plugin-upsun-upsun-cli.yaml +8 -0
  155. package/core/workspace-app/.ddev/docker-compose.screens.yaml +15 -0
  156. package/core/workspace-app/.ddev/nginx_full/nginx-site.conf +114 -0
  157. package/core/workspace-app/.ddev/web-build/Dockerfile.workspace-app +203 -0
  158. package/core/workspace-app/.ddev/web-build/ddev-shim-ddev +122 -0
  159. package/{PLUGINS.md → core/workspace-app/PLUGINS.md} +1 -1
  160. package/core/workspace-app/README.md +380 -0
  161. package/{actions → core/workspace-app/actions}/ddev.js +0 -1
  162. package/{actions → core/workspace-app/actions}/rag.js +0 -1
  163. package/{actions → core/workspace-app/actions}/testing.js +0 -1
  164. package/{assistant.js → core/workspace-app/assistant.js} +1 -10
  165. package/{git-info.js → core/workspace-app/git-info.js} +17 -1
  166. package/core/workspace-app/package.json +29 -0
  167. package/core/workspace-app/plugin-catalog.js +50 -0
  168. package/{plugin-loader.js → core/workspace-app/plugin-loader.js} +19 -1
  169. package/{plugins-page.js → core/workspace-app/plugins-page.js} +28 -2
  170. package/{row-layout.js → core/workspace-app/row-layout.js} +0 -1
  171. package/{server.js → core/workspace-app/server.js} +26 -225
  172. package/core/workspace-app/tests/features/actions-rail.feature +44 -0
  173. package/core/workspace-app/tests/features/ai-items.feature +23 -0
  174. package/core/workspace-app/tests/features/assistant-speech.feature +63 -0
  175. package/core/workspace-app/tests/features/checks.feature +39 -0
  176. package/core/workspace-app/tests/features/clone-project.feature +30 -0
  177. package/core/workspace-app/tests/features/command-authoring.feature +58 -0
  178. package/core/workspace-app/tests/features/dashboard.feature +57 -0
  179. package/core/workspace-app/tests/features/menus.feature +29 -0
  180. package/core/workspace-app/tests/features/plugins/jira/jira.feature +47 -0
  181. package/core/workspace-app/tests/features/plugins.feature +20 -0
  182. package/core/workspace-app/tests/features/project-actions.feature +40 -0
  183. package/core/workspace-app/tests/features/screens.feature +29 -0
  184. package/core/workspace-app/tests/features/settings.feature +46 -0
  185. package/core/workspace-app/tests/features/test-reports.feature +32 -0
  186. package/core/workspace-app/tests/features/theme.feature +40 -0
  187. package/core/workspace-app/tests/step-definitions/actions-post.steps.js +25 -0
  188. package/core/workspace-app/tests/step-definitions/assistant-speech.steps.js +152 -0
  189. package/core/workspace-app/tests/step-definitions/preconditions.steps.js +100 -0
  190. package/core/workspace-app/tests/unit/_isolated-config.js +31 -0
  191. package/core/workspace-app/tests/unit/accessibility.test.js +124 -0
  192. package/core/workspace-app/tests/unit/actions-rail-ux.test.js +56 -0
  193. package/core/workspace-app/tests/unit/actions-rail.test.js +51 -0
  194. package/core/workspace-app/tests/unit/ai-inventory.test.js +64 -0
  195. package/core/workspace-app/tests/unit/app-docs.test.js +64 -0
  196. package/core/workspace-app/tests/unit/assistant-avatar.test.js +110 -0
  197. package/core/workspace-app/tests/unit/assistant-cast.test.js +105 -0
  198. package/core/workspace-app/tests/unit/assistant-send-animation.test.js +15 -0
  199. package/core/workspace-app/tests/unit/assistant-suggestions.test.js +112 -0
  200. package/core/workspace-app/tests/unit/assistant-switches.test.js +53 -0
  201. package/core/workspace-app/tests/unit/assistant-views.test.js +146 -0
  202. package/core/workspace-app/tests/unit/assistant-working-indicator.test.js +49 -0
  203. package/core/workspace-app/tests/unit/assistant.test.js +61 -0
  204. package/core/workspace-app/tests/unit/audio-player.test.js +147 -0
  205. package/core/workspace-app/tests/unit/audio-voice.test.js +634 -0
  206. package/core/workspace-app/tests/unit/auth-google.test.js +180 -0
  207. package/core/workspace-app/tests/unit/autostart-watch.test.js +76 -0
  208. package/core/workspace-app/tests/unit/browsers.test.js +76 -0
  209. package/core/workspace-app/tests/unit/build-distribution.test.js +125 -0
  210. package/core/workspace-app/tests/unit/build-order.test.js +153 -0
  211. package/core/workspace-app/tests/unit/builder-args.test.js +113 -0
  212. package/core/workspace-app/tests/unit/builders.test.js +158 -0
  213. package/core/workspace-app/tests/unit/card-state-toggle.test.js +29 -0
  214. package/core/workspace-app/tests/unit/changelog.test.js +177 -0
  215. package/core/workspace-app/tests/unit/chat-thread.test.js +92 -0
  216. package/core/workspace-app/tests/unit/checks-plan.test.js +205 -0
  217. package/core/workspace-app/tests/unit/claude-args.test.js +182 -0
  218. package/core/workspace-app/tests/unit/claude-code-here.test.js +15 -0
  219. package/core/workspace-app/tests/unit/claude-resume.test.js +147 -0
  220. package/core/workspace-app/tests/unit/claude-screen-options.test.js +84 -0
  221. package/core/workspace-app/tests/unit/claude-session.test.js +41 -0
  222. package/core/workspace-app/tests/unit/command-flags.test.js +45 -0
  223. package/core/workspace-app/tests/unit/command-palette.test.js +105 -0
  224. package/core/workspace-app/tests/unit/commands-center.test.js +338 -0
  225. package/core/workspace-app/tests/unit/composer-menu.test.js +187 -0
  226. package/core/workspace-app/tests/unit/config-yaml.test.js +116 -0
  227. package/core/workspace-app/tests/unit/confirm-dialog.test.js +69 -0
  228. package/core/workspace-app/tests/unit/create-card.test.js +85 -0
  229. package/core/workspace-app/tests/unit/ddev-addons.test.js +144 -0
  230. package/core/workspace-app/tests/unit/ddev-auth-ssh.test.js +40 -0
  231. package/core/workspace-app/tests/unit/ddev-custom-commands.test.js +56 -0
  232. package/core/workspace-app/tests/unit/ddev-diagnose.test.js +57 -0
  233. package/core/workspace-app/tests/unit/ddev-domains.test.js +153 -0
  234. package/core/workspace-app/tests/unit/ddev-hostnames.test.js +143 -0
  235. package/core/workspace-app/tests/unit/ddev-import.test.js +82 -0
  236. package/core/workspace-app/tests/unit/ddev-mail.test.js +135 -0
  237. package/core/workspace-app/tests/unit/ddev-menu-and-create.test.js +28 -0
  238. package/core/workspace-app/tests/unit/ddev-mutagen.test.js +37 -0
  239. package/core/workspace-app/tests/unit/ddev-options.test.js +51 -0
  240. package/core/workspace-app/tests/unit/ddev-share.test.js +40 -0
  241. package/core/workspace-app/tests/unit/ddev-shim.test.js +57 -0
  242. package/core/workspace-app/tests/unit/ddev-snapshots.test.js +142 -0
  243. package/core/workspace-app/tests/unit/ddev-start-daemons.test.js +62 -0
  244. package/core/workspace-app/tests/unit/ddev-xdebug.test.js +41 -0
  245. package/core/workspace-app/tests/unit/deep-chat-origin.test.js +38 -0
  246. package/core/workspace-app/tests/unit/density.test.js +52 -0
  247. package/core/workspace-app/tests/unit/elevenlabs.test.js +453 -0
  248. package/core/workspace-app/tests/unit/event-log.test.js +102 -0
  249. package/core/workspace-app/tests/unit/favicon.test.js +68 -0
  250. package/core/workspace-app/tests/unit/files-browser.test.js +224 -0
  251. package/core/workspace-app/tests/unit/git-info.test.js +26 -0
  252. package/core/workspace-app/tests/unit/grid-and-list.test.js +103 -0
  253. package/core/workspace-app/tests/unit/host-tool-wrapper.test.js +18 -0
  254. package/core/workspace-app/tests/unit/html-patterns.test.js +51 -0
  255. package/core/workspace-app/tests/unit/ide-infra.test.js +28 -0
  256. package/core/workspace-app/tests/unit/images-ai.test.js +201 -0
  257. package/core/workspace-app/tests/unit/images-create.test.js +59 -0
  258. package/core/workspace-app/tests/unit/images-providers.test.js +248 -0
  259. package/core/workspace-app/tests/unit/images-svg.test.js +213 -0
  260. package/core/workspace-app/tests/unit/images-workspace.test.js +144 -0
  261. package/core/workspace-app/tests/unit/jobs-fragment.test.js +66 -0
  262. package/core/workspace-app/tests/unit/media-ux.test.js +164 -0
  263. package/core/workspace-app/tests/unit/modal-fit.test.js +87 -0
  264. package/core/workspace-app/tests/unit/modules-load.test.js +23 -0
  265. package/core/workspace-app/tests/unit/narration.test.js +186 -0
  266. package/core/workspace-app/tests/unit/navigation-editor.test.js +260 -0
  267. package/core/workspace-app/tests/unit/navigation.test.js +254 -0
  268. package/core/workspace-app/tests/unit/new-folder.test.js +121 -0
  269. package/core/workspace-app/tests/unit/new-screen-form.test.js +149 -0
  270. package/core/workspace-app/tests/unit/node-menu.test.js +161 -0
  271. package/core/workspace-app/tests/unit/notes.test.js +128 -0
  272. package/core/workspace-app/tests/unit/page-structure.test.js +40 -0
  273. package/core/workspace-app/tests/unit/page-width.test.js +59 -0
  274. package/core/workspace-app/tests/unit/php-version.test.js +90 -0
  275. package/core/workspace-app/tests/unit/plan-card.test.js +44 -0
  276. package/core/workspace-app/tests/unit/plugin-catalog.test.js +31 -0
  277. package/core/workspace-app/tests/unit/plugin-system.test.js +251 -0
  278. package/core/workspace-app/tests/unit/plugins-page.test.js +28 -0
  279. package/core/workspace-app/tests/unit/plugins.test.js +58 -0
  280. package/core/workspace-app/tests/unit/project-activity-filter.test.js +180 -0
  281. package/core/workspace-app/tests/unit/project-make.test.js +143 -0
  282. package/core/workspace-app/tests/unit/project-name.test.js +202 -0
  283. package/core/workspace-app/tests/unit/reader.test.js +62 -0
  284. package/core/workspace-app/tests/unit/release.test.js +92 -0
  285. package/core/workspace-app/tests/unit/remove-confirm.test.js +37 -0
  286. package/core/workspace-app/tests/unit/report-pdf.test.js +171 -0
  287. package/core/workspace-app/tests/unit/round-11.test.js +31 -0
  288. package/core/workspace-app/tests/unit/round-12.test.js +37 -0
  289. package/core/workspace-app/tests/unit/row-layout.test.js +585 -0
  290. package/core/workspace-app/tests/unit/row-menus-fold.test.js +29 -0
  291. package/core/workspace-app/tests/unit/rows-add-modal.test.js +68 -0
  292. package/core/workspace-app/tests/unit/screen-runner-queue.test.js +41 -0
  293. package/core/workspace-app/tests/unit/screen-window.test.js +116 -0
  294. package/core/workspace-app/tests/unit/screens-beside-running.test.js +19 -0
  295. package/core/workspace-app/tests/unit/screens-columns.test.js +79 -0
  296. package/core/workspace-app/tests/unit/screens-menu.test.js +84 -0
  297. package/core/workspace-app/tests/unit/screens-modal-overlay.test.js +14 -0
  298. package/core/workspace-app/tests/unit/screens-waiting.test.js +129 -0
  299. package/core/workspace-app/tests/unit/screens.test.js +74 -0
  300. package/core/workspace-app/tests/unit/second-storage.test.js +218 -0
  301. package/core/workspace-app/tests/unit/session-name.test.js +64 -0
  302. package/core/workspace-app/tests/unit/settings-file.test.js +173 -0
  303. package/core/workspace-app/tests/unit/settings-nested-list.test.js +54 -0
  304. package/core/workspace-app/tests/unit/settings-raw.test.js +177 -0
  305. package/core/workspace-app/tests/unit/shared-prefs.test.js +49 -0
  306. package/core/workspace-app/tests/unit/speech.test.js +290 -0
  307. package/core/workspace-app/tests/unit/static-caching.test.js +39 -0
  308. package/core/workspace-app/tests/unit/status-language.test.js +47 -0
  309. package/core/workspace-app/tests/unit/tab-modal.test.js +69 -0
  310. package/core/workspace-app/tests/unit/tab-title.test.js +37 -0
  311. package/core/workspace-app/tests/unit/template-literals.test.js +52 -0
  312. package/core/workspace-app/tests/unit/terminal-scrollback.test.js +61 -0
  313. package/core/workspace-app/tests/unit/terminal-size.test.js +103 -0
  314. package/core/workspace-app/tests/unit/terminal-style.test.js +84 -0
  315. package/core/workspace-app/tests/unit/terminal-windows.test.js +154 -0
  316. package/core/workspace-app/tests/unit/themes.test.js +453 -0
  317. package/core/workspace-app/tests/unit/tool-labels.test.js +34 -0
  318. package/core/workspace-app/tests/unit/topbar-icons.test.js +47 -0
  319. package/core/workspace-app/tests/unit/update-docker-gid.test.js +51 -0
  320. package/core/workspace-app/tests/unit/user-guide.test.js +60 -0
  321. package/core/workspace-app/tests/unit/version-check.test.js +98 -0
  322. package/core/workspace-app/tests/unit/version-link.test.js +50 -0
  323. package/core/workspace-app/tests/unit/video-borrow.test.js +198 -0
  324. package/core/workspace-app/tests/unit/video-edit-ask.test.js +93 -0
  325. package/core/workspace-app/tests/unit/video-edit-short.test.js +42 -0
  326. package/core/workspace-app/tests/unit/video-edit.test.js +417 -0
  327. package/core/workspace-app/tests/unit/video-transcript.test.js +237 -0
  328. package/core/workspace-app/tests/unit/waiting.test.js +48 -0
  329. package/core/workspace-app/tests/unit/wordpress-odoo-builders.test.js +38 -0
  330. package/core/workspace-app/tests/unit/workspace-bin.test.js +48 -0
  331. package/core/workspace-app/tests/unit/workspace-card-tools.test.js +30 -0
  332. package/core/workspace-app/tests/unit/workspace-delete.test.js +77 -0
  333. package/core/workspace-app/tests/unit/workspace-types.test.js +68 -0
  334. package/core/workspace-app/tests/unit/worktree-clone.test.js +250 -0
  335. package/{workspace-types.js → core/workspace-app/workspace-types.js} +2 -2
  336. package/{workspaces.js → core/workspace-app/workspaces.js} +1 -5
  337. package/package.json +38 -36
  338. package/actions/graphs.js +0 -107
  339. package/actions/obsidian.js +0 -59
  340. package/graphs.js +0 -104
  341. package/obsidian.js +0 -405
  342. /package/{WORKSPACES.md → core/workspace-app/WORKSPACES.md} +0 -0
  343. /package/{actions → core/workspace-app/actions}/ai-items.js +0 -0
  344. /package/{actions → core/workspace-app/actions}/audio.js +0 -0
  345. /package/{actions → core/workspace-app/actions}/build-order.js +0 -0
  346. /package/{actions → core/workspace-app/actions}/checks.js +0 -0
  347. /package/{actions → core/workspace-app/actions}/claude-session.js +0 -0
  348. /package/{actions → core/workspace-app/actions}/commands-center.js +0 -0
  349. /package/{actions → core/workspace-app/actions}/composer.js +0 -0
  350. /package/{actions → core/workspace-app/actions}/ddev-addons.js +0 -0
  351. /package/{actions → core/workspace-app/actions}/ddev-mail.js +0 -0
  352. /package/{actions → core/workspace-app/actions}/folders.js +0 -0
  353. /package/{actions → core/workspace-app/actions}/images-svg.js +0 -0
  354. /package/{actions → core/workspace-app/actions}/images.js +0 -0
  355. /package/{actions → core/workspace-app/actions}/navigation.js +0 -0
  356. /package/{actions → core/workspace-app/actions}/node.js +0 -0
  357. /package/{actions → core/workspace-app/actions}/notes.js +0 -0
  358. /package/{actions → core/workspace-app/actions}/plugins.js +0 -0
  359. /package/{actions → core/workspace-app/actions}/project-make.js +0 -0
  360. /package/{actions → core/workspace-app/actions}/row-actions.js +0 -0
  361. /package/{actions → core/workspace-app/actions}/screens.js +0 -0
  362. /package/{actions → core/workspace-app/actions}/video-edit.js +0 -0
  363. /package/{ai-inventory.js → core/workspace-app/ai-inventory.js} +0 -0
  364. /package/{ai-items.js → core/workspace-app/ai-items.js} +0 -0
  365. /package/{assistant-cast.js → core/workspace-app/assistant-cast.js} +0 -0
  366. /package/{assistant-suggestions.js → core/workspace-app/assistant-suggestions.js} +0 -0
  367. /package/{audio-voice.js → core/workspace-app/audio-voice.js} +0 -0
  368. /package/{auth-google.js → core/workspace-app/auth-google.js} +0 -0
  369. /package/{build-ai.js → core/workspace-app/build-ai.js} +0 -0
  370. /package/{build-order-page.js → core/workspace-app/build-order-page.js} +0 -0
  371. /package/{build-order.js → core/workspace-app/build-order.js} +0 -0
  372. /package/{builder-args.js → core/workspace-app/builder-args.js} +0 -0
  373. /package/{changelog.js → core/workspace-app/changelog.js} +0 -0
  374. /package/{chat-threads.js → core/workspace-app/chat-threads.js} +0 -0
  375. /package/{checks.js → core/workspace-app/checks.js} +0 -0
  376. /package/{claude-args.js → core/workspace-app/claude-args.js} +0 -0
  377. /package/{claude-sessions.js → core/workspace-app/claude-sessions.js} +0 -0
  378. /package/{command-author.js → core/workspace-app/command-author.js} +0 -0
  379. /package/{command-block.js → core/workspace-app/command-block.js} +0 -0
  380. /package/{commands → core/workspace-app/commands}/defaults.yml +0 -0
  381. /package/{commands-page.js → core/workspace-app/commands-page.js} +0 -0
  382. /package/{commands.js → core/workspace-app/commands.js} +0 -0
  383. /package/{cucumber.js → core/workspace-app/cucumber.js} +0 -0
  384. /package/{ddev-addons-page.js → core/workspace-app/ddev-addons-page.js} +0 -0
  385. /package/{ddev-addons.js → core/workspace-app/ddev-addons.js} +0 -0
  386. /package/{ddev-custom-commands.js → core/workspace-app/ddev-custom-commands.js} +0 -0
  387. /package/{ddev-domains.js → core/workspace-app/ddev-domains.js} +0 -0
  388. /package/{ddev-import.js → core/workspace-app/ddev-import.js} +0 -0
  389. /package/{ddev-mail-page.js → core/workspace-app/ddev-mail-page.js} +0 -0
  390. /package/{ddev-mail.js → core/workspace-app/ddev-mail.js} +0 -0
  391. /package/{ddev-options.js → core/workspace-app/ddev-options.js} +0 -0
  392. /package/{ddev-snapshots.js → core/workspace-app/ddev-snapshots.js} +0 -0
  393. /package/{ddev.js → core/workspace-app/ddev.js} +0 -0
  394. /package/{docs → core/workspace-app/docs}/DEVELOPER-GUIDE.md +0 -0
  395. /package/{docs → core/workspace-app/docs}/README.md +0 -0
  396. /package/{docs → core/workspace-app/docs}/REFERENCE.md +0 -0
  397. /package/{docs → core/workspace-app/docs}/USER-GUIDE.md +0 -0
  398. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-board.png +0 -0
  399. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-coffee.png +0 -0
  400. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-colours.png +0 -0
  401. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-icons.png +0 -0
  402. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-moves.png +0 -0
  403. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-pick-cookieman.png +0 -0
  404. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-settings-voices.png +0 -0
  405. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-status.png +0 -0
  406. /package/{docs → core/workspace-app/docs}/screenshots/create-card/create-card.png +0 -0
  407. /package/{docs → core/workspace-app/docs}/screenshots/create-card/start-ai.png +0 -0
  408. /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-address.png +0 -0
  409. /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-worktree-off.png +0 -0
  410. /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-worktree-on.png +0 -0
  411. /package/{docs → core/workspace-app/docs}/screenshots/guide-ddev-menu.png +0 -0
  412. /package/{docs → core/workspace-app/docs}/screenshots/guide-domains.png +0 -0
  413. /package/{docs → core/workspace-app/docs}/screenshots/guide-home.png +0 -0
  414. /package/{docs → core/workspace-app/docs}/screenshots/guide-ide-menu.png +0 -0
  415. /package/{docs → core/workspace-app/docs}/screenshots/guide-remove-refusal.png +0 -0
  416. /package/{docs → core/workspace-app/docs}/screenshots/guide-rows-worktree.png +0 -0
  417. /package/{docs → core/workspace-app/docs}/screenshots/guide-screens.png +0 -0
  418. /package/{docs → core/workspace-app/docs}/screenshots/guide-settings.png +0 -0
  419. /package/{docs → core/workspace-app/docs}/screenshots/guide-workspace-page.png +0 -0
  420. /package/{docs → core/workspace-app/docs}/screenshots/home/card-settings.png +0 -0
  421. /package/{docs → core/workspace-app/docs}/screenshots/home/drag-reorder.png +0 -0
  422. /package/{docs → core/workspace-app/docs}/screenshots/home/edit-card.png +0 -0
  423. /package/{docs → core/workspace-app/docs}/screenshots/images/claude-code-image.png +0 -0
  424. /package/{docs → core/workspace-app/docs}/screenshots/images/edit-with-ai.png +0 -0
  425. /package/{docs → core/workspace-app/docs}/screenshots/images/gallery.png +0 -0
  426. /package/{docs → core/workspace-app/docs}/screenshots/images/generate-description.png +0 -0
  427. /package/{docs → core/workspace-app/docs}/screenshots/images/generate-plan.png +0 -0
  428. /package/{docs → core/workspace-app/docs}/screenshots/images/providers.png +0 -0
  429. /package/{docs → core/workspace-app/docs}/screenshots/images/svg-form.png +0 -0
  430. /package/{docs → core/workspace-app/docs}/screenshots/images/svg-result.png +0 -0
  431. /package/{docs → core/workspace-app/docs}/screenshots/images/upload.png +0 -0
  432. /package/{docs → core/workspace-app/docs}/screenshots/new-folder/2-form.png +0 -0
  433. /package/{docs → core/workspace-app/docs}/screenshots/new-folder/8-ddev-options.png +0 -0
  434. /package/{docs → core/workspace-app/docs}/screenshots/new-folder/files-folder.png +0 -0
  435. /package/{docs → core/workspace-app/docs}/screenshots/projects/ddev-custom-commands.png +0 -0
  436. /package/{docs → core/workspace-app/docs}/screenshots/projects/state-chip-stop.png +0 -0
  437. /package/{docs → core/workspace-app/docs}/screenshots/readme/dashboard-dark.png +0 -0
  438. /package/{docs → core/workspace-app/docs}/screenshots/readme/dashboard-light.png +0 -0
  439. /package/{docs → core/workspace-app/docs}/screenshots/readme/dev-dark.png +0 -0
  440. /package/{docs → core/workspace-app/docs}/screenshots/readme/dev-light.png +0 -0
  441. /package/{docs → core/workspace-app/docs}/screenshots/readme/docs-dark.png +0 -0
  442. /package/{docs → core/workspace-app/docs}/screenshots/readme/docs-light.png +0 -0
  443. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/ai.svg +0 -0
  444. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/code.svg +0 -0
  445. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/docs.svg +0 -0
  446. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/services.svg +0 -0
  447. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/sites.svg +0 -0
  448. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-report-dark.png +0 -0
  449. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-report-light.png +0 -0
  450. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-scenarios.png +0 -0
  451. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-steps.png +0 -0
  452. /package/{docs → core/workspace-app/docs}/screenshots/readme/videos-dark.png +0 -0
  453. /package/{docs → core/workspace-app/docs}/screenshots/readme/videos-light.png +0 -0
  454. /package/{docs → core/workspace-app/docs}/screenshots/screens-menu.png +0 -0
  455. /package/{docs → core/workspace-app/docs}/screenshots/screens-new.png +0 -0
  456. /package/{docs → core/workspace-app/docs}/screenshots/settings/auth-google-lists.png +0 -0
  457. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-1-row.png +0 -0
  458. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-2-confirm.png +0 -0
  459. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-3-type-name.png +0 -0
  460. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-4-type-yes.png +0 -0
  461. /package/{event-log.js → core/workspace-app/event-log.js} +0 -0
  462. /package/{files-browser.js → core/workspace-app/files-browser.js} +0 -0
  463. /package/{folders.js → core/workspace-app/folders.js} +0 -0
  464. /package/{format.js → core/workspace-app/format.js} +0 -0
  465. /package/{github-auth.js → core/workspace-app/github-auth.js} +0 -0
  466. /package/{html.js → core/workspace-app/html.js} +0 -0
  467. /package/{icons.js → core/workspace-app/icons.js} +0 -0
  468. /package/{images-ai.js → core/workspace-app/images-ai.js} +0 -0
  469. /package/{images-create.js → core/workspace-app/images-create.js} +0 -0
  470. /package/{images-page.js → core/workspace-app/images-page.js} +0 -0
  471. /package/{images-records.js → core/workspace-app/images-records.js} +0 -0
  472. /package/{images-render.js → core/workspace-app/images-render.js} +0 -0
  473. /package/{images-svg-core.js → core/workspace-app/images-svg-core.js} +0 -0
  474. /package/{images-svg-sanitize.js → core/workspace-app/images-svg-sanitize.js} +0 -0
  475. /package/{images-svg.js → core/workspace-app/images-svg.js} +0 -0
  476. /package/{images.js → core/workspace-app/images.js} +0 -0
  477. /package/{jobs.js → core/workspace-app/jobs.js} +0 -0
  478. /package/{linger.js → core/workspace-app/linger.js} +0 -0
  479. /package/{lists.js → core/workspace-app/lists.js} +0 -0
  480. /package/{media-info.js → core/workspace-app/media-info.js} +0 -0
  481. /package/{navigation-page.js → core/workspace-app/navigation-page.js} +0 -0
  482. /package/{navigation.js → core/workspace-app/navigation.js} +0 -0
  483. /package/{notes.js → core/workspace-app/notes.js} +0 -0
  484. /package/{palette-index.js → core/workspace-app/palette-index.js} +0 -0
  485. /package/{playwright.config.js → core/workspace-app/playwright.config.js} +0 -0
  486. /package/{plugin-install.js → core/workspace-app/plugin-install.js} +0 -0
  487. /package/{plugin-manifest.js → core/workspace-app/plugin-manifest.js} +0 -0
  488. /package/{plugin-settings.js → core/workspace-app/plugin-settings.js} +0 -0
  489. /package/{plugins → core/workspace-app/plugins}/workspace-notes/index.js +0 -0
  490. /package/{project-make.js → core/workspace-app/project-make.js} +0 -0
  491. /package/{project-name.js → core/workspace-app/project-name.js} +0 -0
  492. /package/{public → core/workspace-app/public}/assistant-avatar-moves.js +0 -0
  493. /package/{public → core/workspace-app/public}/assistant-avatar-ui.js +0 -0
  494. /package/{public → core/workspace-app/public}/assistant-avatar.js +0 -0
  495. /package/{public → core/workspace-app/public}/avatar/druplicon.vrm +0 -0
  496. /package/{public → core/workspace-app/public}/css/base.css +0 -0
  497. /package/{public → core/workspace-app/public}/css/components/ai-assistant.css +0 -0
  498. /package/{public → core/workspace-app/public}/css/components/assistant-avatar.css +0 -0
  499. /package/{public → core/workspace-app/public}/css/components/assistant-views.css +0 -0
  500. /package/{public → core/workspace-app/public}/css/components/build-order.css +0 -0
  501. /package/{public → core/workspace-app/public}/css/components/clone-as-a-git-worktree.css +0 -0
  502. /package/{public → core/workspace-app/public}/css/components/command-palette.css +0 -0
  503. /package/{public → core/workspace-app/public}/css/components/commands-center.css +0 -0
  504. /package/{public → core/workspace-app/public}/css/components/confirm-dialog.css +0 -0
  505. /package/{public → core/workspace-app/public}/css/components/create-card.css +0 -0
  506. /package/{public → core/workspace-app/public}/css/components/ddev-menu.css +0 -0
  507. /package/{public → core/workspace-app/public}/css/components/deep-chat.css +0 -0
  508. /package/{public → core/workspace-app/public}/css/components/density.css +0 -0
  509. /package/{public → core/workspace-app/public}/css/components/floating-assistant-workspace-pages.css +0 -0
  510. /package/{public → core/workspace-app/public}/css/components/folders.css +0 -0
  511. /package/{public → core/workspace-app/public}/css/components/full-height-left-assistant-sidebar-all-pages.css +0 -0
  512. /package/{public → core/workspace-app/public}/css/components/full-height-right-actions-rail-pages-that-have-actions.css +0 -0
  513. /package/{public → core/workspace-app/public}/css/components/grid-and-list.css +0 -0
  514. /package/{public → core/workspace-app/public}/css/components/help-page.css +0 -0
  515. /package/{public → core/workspace-app/public}/css/components/hero.css +0 -0
  516. /package/{public → core/workspace-app/public}/css/components/images.css +0 -0
  517. /package/{public → core/workspace-app/public}/css/components/layout-rhythm-and-spacing-polish.css +0 -0
  518. /package/{public → core/workspace-app/public}/css/components/mic-on-any-field-that-wants-typing.css +0 -0
  519. /package/{public → core/workspace-app/public}/css/components/modal-fit.css +0 -0
  520. /package/{public → core/workspace-app/public}/css/components/page-width.css +0 -0
  521. /package/{public → core/workspace-app/public}/css/components/plugins-page.css +0 -0
  522. /package/{public → core/workspace-app/public}/css/components/project-cards.css +0 -0
  523. /package/{public → core/workspace-app/public}/css/components/project-rows.css +0 -0
  524. /package/{public → core/workspace-app/public}/css/components/reordering-the-workspace-cards.css +0 -0
  525. /package/{public → core/workspace-app/public}/css/components/responsive-home-and-the-workspace-pages.css +0 -0
  526. /package/{public → core/workspace-app/public}/css/components/running-commands-as-toasts.css +0 -0
  527. /package/{public → core/workspace-app/public}/css/components/settings-editor-core-config-yml.css +0 -0
  528. /package/{public → core/workspace-app/public}/css/components/short-results-as-a-toast-at-the-top.css +0 -0
  529. /package/{public → core/workspace-app/public}/css/components/sound-player.css +0 -0
  530. /package/{public → core/workspace-app/public}/css/components/suggest-agents-skills-commands.css +0 -0
  531. /package/{public → core/workspace-app/public}/css/components/tab-modal.css +0 -0
  532. /package/{public → core/workspace-app/public}/css/components/terminals.css +0 -0
  533. /package/{public → core/workspace-app/public}/css/components/test-report-media-one-file-per-row-paged.css +0 -0
  534. /package/{public → core/workspace-app/public}/css/components/top-toolbar-logo-breadcrumb.css +0 -0
  535. /package/{public → core/workspace-app/public}/css/components/video-edit.css +0 -0
  536. /package/{public → core/workspace-app/public}/css/components/web-ide-open-with.css +0 -0
  537. /package/{public → core/workspace-app/public}/css/components/working-indicators.css +0 -0
  538. /package/{public → core/workspace-app/public}/css/components/workspace-cards.css +0 -0
  539. /package/{public → core/workspace-app/public}/favicon.ico +0 -0
  540. /package/{public → core/workspace-app/public}/favicon.png +0 -0
  541. /package/{public → core/workspace-app/public}/htmx.min.js +0 -0
  542. /package/{public → core/workspace-app/public}/logo.png +0 -0
  543. /package/{public → core/workspace-app/public}/media-player.js +0 -0
  544. /package/{public → core/workspace-app/public}/mic.js +0 -0
  545. /package/{public → core/workspace-app/public}/narration.js +0 -0
  546. /package/{public → core/workspace-app/public}/palette.js +0 -0
  547. /package/{public → core/workspace-app/public}/sortable.js +0 -0
  548. /package/{public → core/workspace-app/public}/suggest.js +0 -0
  549. /package/{public → core/workspace-app/public}/terminal.js +0 -0
  550. /package/{public → core/workspace-app/public}/themes/default/theme.css +0 -0
  551. /package/{public → core/workspace-app/public}/themes/ember/theme.css +0 -0
  552. /package/{public → core/workspace-app/public}/themes/press/theme.css +0 -0
  553. /package/{public → core/workspace-app/public}/themes/slate/theme.css +0 -0
  554. /package/{public → core/workspace-app/public}/touch-icon.png +0 -0
  555. /package/{public → core/workspace-app/public}/ui.js +0 -0
  556. /package/{public → core/workspace-app/public}/vendor/ace/LICENSE.txt +0 -0
  557. /package/{public → core/workspace-app/public}/vendor/ace/ace.js +0 -0
  558. /package/{public → core/workspace-app/public}/vendor/ace/ext-language_tools.js +0 -0
  559. /package/{public → core/workspace-app/public}/vendor/ace/ext-searchbox.js +0 -0
  560. /package/{public → core/workspace-app/public}/vendor/ace/mode-css.js +0 -0
  561. /package/{public → core/workspace-app/public}/vendor/ace/mode-html.js +0 -0
  562. /package/{public → core/workspace-app/public}/vendor/ace/mode-javascript.js +0 -0
  563. /package/{public → core/workspace-app/public}/vendor/ace/mode-markdown.js +0 -0
  564. /package/{public → core/workspace-app/public}/vendor/ace/mode-sh.js +0 -0
  565. /package/{public → core/workspace-app/public}/vendor/ace/mode-yaml.js +0 -0
  566. /package/{public → core/workspace-app/public}/vendor/ace/theme-textmate.js +0 -0
  567. /package/{public → core/workspace-app/public}/vendor/ace/theme-tomorrow_night.js +0 -0
  568. /package/{public → core/workspace-app/public}/vendor/ace/worker-yaml.js +0 -0
  569. /package/{public → core/workspace-app/public}/vendor/deep-chat.LICENSE +0 -0
  570. /package/{public → core/workspace-app/public}/vendor/deep-chat.bundle.js +0 -0
  571. /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-LICENSE.txt +0 -0
  572. /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-latin-400-italic.woff2 +0 -0
  573. /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-latin-400-normal.woff2 +0 -0
  574. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-LICENSE.txt +0 -0
  575. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-400-italic.woff2 +0 -0
  576. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-400-normal.woff2 +0 -0
  577. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-600-italic.woff2 +0 -0
  578. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-600-normal.woff2 +0 -0
  579. /package/{public → core/workspace-app/public}/vendor/fonts/jetbrains-mono-LICENSE.txt +0 -0
  580. /package/{public → core/workspace-app/public}/vendor/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  581. /package/{public → core/workspace-app/public}/vendor/fonts/ubuntu-mono-LICENSE.txt +0 -0
  582. /package/{public → core/workspace-app/public}/vendor/fonts/ubuntu-mono-latin-wght-normal.woff2 +0 -0
  583. /package/{public → core/workspace-app/public}/vendor/tabler-LICENSE.txt +0 -0
  584. /package/{public → core/workspace-app/public}/vendor/tabler-sprite.svg +0 -0
  585. /package/{public → core/workspace-app/public}/vendor/three/LICENSE +0 -0
  586. /package/{public → core/workspace-app/public}/vendor/three/addons/loaders/GLTFLoader.js +0 -0
  587. /package/{public → core/workspace-app/public}/vendor/three/addons/utils/BufferGeometryUtils.js +0 -0
  588. /package/{public → core/workspace-app/public}/vendor/three/addons/utils/SkeletonUtils.js +0 -0
  589. /package/{public → core/workspace-app/public}/vendor/three/three.core.min.js +0 -0
  590. /package/{public → core/workspace-app/public}/vendor/three/three.module.min.js +0 -0
  591. /package/{public → core/workspace-app/public}/vendor/three-vrm/LICENSE +0 -0
  592. /package/{public → core/workspace-app/public}/vendor/three-vrm/three-vrm.module.min.js +0 -0
  593. /package/{public → core/workspace-app/public}/vendor/uikit-icons.min.js +0 -0
  594. /package/{public → core/workspace-app/public}/vendor/uikit.min.css +0 -0
  595. /package/{public → core/workspace-app/public}/vendor/uikit.min.js +0 -0
  596. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/bookmark-outlined.svg +0 -0
  597. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/headset.svg +0 -0
  598. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/menu.svg +0 -0
  599. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/pause.svg +0 -0
  600. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/play.svg +0 -0
  601. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/share.svg +0 -0
  602. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/sound-off.svg +0 -0
  603. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/sound-on.svg +0 -0
  604. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/stop.svg +0 -0
  605. /package/{public → core/workspace-app/public}/vendor/xterm/addon-fit.js +0 -0
  606. /package/{public → core/workspace-app/public}/vendor/xterm/addon-unicode11.js +0 -0
  607. /package/{public → core/workspace-app/public}/vendor/xterm/addon-web-links.js +0 -0
  608. /package/{public → core/workspace-app/public}/vendor/xterm/xterm.css +0 -0
  609. /package/{public → core/workspace-app/public}/vendor/xterm/xterm.js +0 -0
  610. /package/{rag.js → core/workspace-app/rag.js} +0 -0
  611. /package/{reader.js → core/workspace-app/reader.js} +0 -0
  612. /package/{repo-blobs.js → core/workspace-app/repo-blobs.js} +0 -0
  613. /package/{report-pdf.js → core/workspace-app/report-pdf.js} +0 -0
  614. /package/{screenrc → core/workspace-app/screenrc} +0 -0
  615. /package/{screens.js → core/workspace-app/screens.js} +0 -0
  616. /package/{scripts → core/workspace-app/scripts}/code-server-start.sh +0 -0
  617. /package/{scripts → core/workspace-app/scripts}/images-claude-finish.js +0 -0
  618. /package/{scripts → core/workspace-app/scripts}/screen-host.sh +0 -0
  619. /package/{scripts → core/workspace-app/scripts}/screen-runner.js +0 -0
  620. /package/{scripts → core/workspace-app/scripts}/screen-then.js +0 -0
  621. /package/{scripts → core/workspace-app/scripts}/update.sh +0 -0
  622. /package/{settings-file.js → core/workspace-app/settings-file.js} +0 -0
  623. /package/{speech.js → core/workspace-app/speech.js} +0 -0
  624. /package/{tab-modal.js → core/workspace-app/tab-modal.js} +0 -0
  625. /package/{test-reports.js → core/workspace-app/test-reports.js} +0 -0
  626. /package/{themes.js → core/workspace-app/themes.js} +0 -0
  627. /package/{transcripts.js → core/workspace-app/transcripts.js} +0 -0
  628. /package/{urls.js → core/workspace-app/urls.js} +0 -0
  629. /package/{validate.js → core/workspace-app/validate.js} +0 -0
  630. /package/{version-check.js → core/workspace-app/version-check.js} +0 -0
  631. /package/{video-edit.js → core/workspace-app/video-edit.js} +0 -0
  632. /package/{waiting.js → core/workspace-app/waiting.js} +0 -0
  633. /package/{workspace-card-tools.js → core/workspace-app/workspace-card-tools.js} +0 -0
  634. /package/{workspace-delete.js → core/workspace-app/workspace-delete.js} +0 -0
@@ -0,0 +1,113 @@
1
+ /**
2
+ * The Build form's Arguments panel must offer exactly what each builder accepts.
3
+ *
4
+ * Ground truth is the builder's own argparse: every arg source in the tree is run
5
+ * with --help and its flags compared with what builder-args.js returns. The old
6
+ * parser guessed the arg file from distribution_name and was wrong for 96
7
+ * builders — a panel full of flags for the Drupal CMS site-template builders, whose
8
+ * parser takes a name and nothing else, and flags the Webship site-template parser
9
+ * refuses outright.
10
+ */
11
+
12
+ const assert = require('node:assert');
13
+ const { test } = require('node:test');
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const { spawnSync } = require('child_process');
17
+ const { parseBuilderArgs, argSourceOf } = require('../../builder-args');
18
+
19
+ const ROOT = path.join(__dirname, '..', '..', '..', '..');
20
+ const SCRIPTS = path.join(ROOT, 'core', 'scripts');
21
+
22
+ function builders() {
23
+ const out = [];
24
+ for (const w of fs.readdirSync(ROOT)) {
25
+ const dir = path.join(ROOT, w);
26
+ let names = [];
27
+ try { names = fs.readdirSync(dir); } catch (_) { continue; }
28
+ for (const f of names) if (/^cmd-.*project.*\.sh$/.test(f)) out.push({ w, dir, f });
29
+ }
30
+ return out;
31
+ }
32
+
33
+ /** The flags a builder's real parser prints for --help, without running the builder. */
34
+ function realFlags({ w, dir, f }) {
35
+ const src = fs.readFileSync(path.join(dir, f), 'utf8');
36
+ const where = argSourceOf(src, ROOT);
37
+ if (!where) return null;
38
+ // Replay the builder's own assignments so an unquoted heredoc expands as it would.
39
+ const assigns = [...src.matchAll(/^[ \t]*([A-Za-z_][A-Za-z0-9_]*)="([^"$`]*)";?[ \t]*(?:#.*)?$/gm)]
40
+ .map((m) => `${m[1]}=${JSON.stringify(m[2])}`).join('; ');
41
+ const settings = path.join(ROOT, 'core', 'config', `workspace.${w}.settings.yml`);
42
+ const parser = where.kind === 'inline'
43
+ ? `bash ${JSON.stringify(path.join(dir, f))} --help`
44
+ : `source ${JSON.stringify(where.file)}`;
45
+ const script = `
46
+ WORKSPACE_SCRIPTS=${JSON.stringify(SCRIPTS)}; source "$WORKSPACE_SCRIPTS/bootstrap.sh" >/dev/null 2>&1
47
+ [ -f ${JSON.stringify(settings)} ] && eval $(parse_yaml ${JSON.stringify(settings)})
48
+ ${assigns}
49
+ set -- probe --help
50
+ ${parser}`;
51
+ const r = spawnSync('bash', ['-c', script], { encoding: 'utf8', timeout: 20000, stdio: ['ignore', 'pipe', 'pipe'] });
52
+ const help = `${r.stdout}\n${r.stderr}`;
53
+ return new Set([...help.matchAll(/(?:^|[\s,\[])(--[a-z][a-z0-9-]*)/g)].map((m) => m[1]).filter((x) => x !== '--help'));
54
+ }
55
+
56
+ test('every builder: the panel offers exactly the flags its real parser accepts', () => {
57
+ const all = builders();
58
+ assert.ok(all.length > 100, `expected the whole tree, found ${all.length} builders`);
59
+ // One --help per distinct source: the flag SET depends only on the file.
60
+ const bySource = new Map();
61
+ const failures = [];
62
+ for (const b of all) {
63
+ const src = fs.readFileSync(path.join(b.dir, b.f), 'utf8');
64
+ const where = argSourceOf(src, ROOT);
65
+ const key = where ? (where.kind === 'inline' ? `inline:${b.w}/${b.f}` : where.file) : null;
66
+ if (!key) continue;
67
+ if (!bySource.has(key)) bySource.set(key, realFlags(b));
68
+ const expected = bySource.get(key);
69
+ if (!expected || !expected.size) continue;
70
+ const got = new Set(parseBuilderArgs(b.dir, b.f, ROOT).map((a) => a.flag));
71
+ const missing = [...expected].filter((x) => !got.has(x));
72
+ const extra = [...got].filter((x) => !expected.has(x));
73
+ if (missing.length || extra.length) failures.push(`${b.w}/${b.f}: missing ${missing.join(' ') || '-'}; refused-by-parser ${extra.join(' ') || '-'}`);
74
+ }
75
+ assert.deepStrictEqual(failures, [], failures.slice(0, 12).join('\n'));
76
+ });
77
+
78
+ test('Drupal CMS site-template builders take a name and nothing else', () => {
79
+ // arg-site-template.sh declares only PROJECT_NAME: the template decides what the site is, so
80
+ // there is nothing left to choose, and a panel that offered --install or --php-version would be
81
+ // offering flags the build exits on.
82
+ const dir = path.join(ROOT, 'site-templates');
83
+ for (const f of ['cmd-byte-project.sh', 'cmd-haven-project.sh', 'cmd-website_starter-project.sh']) {
84
+ assert.deepStrictEqual(parseBuilderArgs(dir, f, ROOT), [], `${f} takes no flags`);
85
+ }
86
+ });
87
+
88
+ test('Webship site-template builders: no flags their parser refuses, and only the ones it takes', () => {
89
+ const dir = path.join(ROOT, 'site-templates');
90
+ const flagsOf = (f) => parseBuilderArgs(dir, f, ROOT).map((x) => x.flag).sort();
91
+ // The Website line's parser takes the users and the pending fixes, nothing from the distribution set.
92
+ assert.deepStrictEqual(flagsOf('cmd-website_starter-1-0-x-project.sh'), ['--add-users', '--with-pending-fixes']);
93
+ assert.deepStrictEqual(flagsOf('cmd-webship_portal-1-0-x-project.sh'), ['--add-users', '--with-pending-fixes']);
94
+ // The Webships line's parser takes the users alone.
95
+ assert.deepStrictEqual(flagsOf('cmd-webships_starter-1-0-x-project.sh'), ['--add-users']);
96
+ for (const refused of ['--install', '--enable', '--skip-set-default-settings', '--skip-drop-database', '--php-version']) {
97
+ assert.ok(!flagsOf('cmd-website_starter-1-0-x-project.sh').includes(refused),
98
+ `${refused} would make the build exit with "unrecognized arguments"`);
99
+ }
100
+ });
101
+
102
+ test('no default is ever shown as unexpanded shell text', () => {
103
+ for (const b of builders()) {
104
+ for (const a of parseBuilderArgs(b.dir, b.f, ROOT)) {
105
+ assert.ok(!String(a.def).includes('${'), `${b.w}/${b.f} ${a.flag} shows "${a.def}"`);
106
+ }
107
+ }
108
+ });
109
+
110
+ test('builders that source through distribution_name are unchanged', () => {
111
+ const flags = parseBuilderArgs(path.join(ROOT, 'dev'), 'cmd-webship11-0-x-project.sh', ROOT).map((x) => x.flag);
112
+ for (const f of ['--install', '--add-users', '--enable', '--php-version']) assert.ok(flags.includes(f), `webship11 lost ${f}`);
113
+ });
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Which cmd-*.sh the Build a new project picker offers.
3
+ *
4
+ * Getting this wrong is invisible: a builder that is filtered out does not error, it is simply not
5
+ * in the list, and nobody notices until somebody goes looking for the one they came for. That is
6
+ * exactly what happened — `cmd-automated-testing-*-project.sh` was excluded by name, so the test
7
+ * workspace offered nine of its fourteen builders and the five missing ones were the reason to open
8
+ * that page at all.
9
+ */
10
+
11
+ const { test } = require('node:test');
12
+ const assert = require('node:assert');
13
+ const fs = require('fs');
14
+ const os = require('os');
15
+ const path = require('path');
16
+
17
+ const { findBuilderScripts, builderLabel } = require('../../workspaces');
18
+
19
+ function folder(names) {
20
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ws-builders-'));
21
+ for (const [name, body] of Object.entries(names)) fs.writeFileSync(path.join(dir, name), body);
22
+ return dir;
23
+ }
24
+
25
+ test('a builder is a cmd-*-project.sh that is not a per-project tool', () => {
26
+ const dir = folder({
27
+ 'cmd-webship11-0-x-project.sh': '#!/usr/bin/env bash\n',
28
+ 'cmd-drupal11-recommended-project.sh': '#!/usr/bin/env bash\n',
29
+ // Tools, however they end: backup and graphify act ON a project, they do not build one.
30
+ 'cmd-tools-backup-dev.sh': '#!/usr/bin/env bash\n',
31
+ 'cmd-tool-something-project.sh': '#!/usr/bin/env bash\n',
32
+ 'cmd-tools-graphify.sh': '#!/usr/bin/env bash\n',
33
+ // Not a command at all.
34
+ 'AGENTS.md': '# x\n',
35
+ });
36
+ assert.deepEqual(findBuilderScripts(dir),
37
+ ['cmd-drupal11-recommended-project.sh', 'cmd-webship11-0-x-project.sh']);
38
+ });
39
+
40
+ test('an automated-testing builder is a builder', () => {
41
+ // It builds a whole site and scaffolds its testing stack on top, which is why the test workspace
42
+ // exists. It was excluded by name, and the list it was missing from was the one that needed it.
43
+ const dir = folder({
44
+ 'cmd-automated-testing-webship11-0-x-project.sh': '#!/usr/bin/env bash\n\n# workspace-name: Webship 11.0.x + Automated Testing (webship-js)\n',
45
+ 'cmd-webship11-0-x-project.sh': '#!/usr/bin/env bash\n\n# workspace-name: Webship 11.0.x\n',
46
+ });
47
+ const found = findBuilderScripts(dir);
48
+ assert.equal(found.length, 2);
49
+ assert.ok(found.includes('cmd-automated-testing-webship11-0-x-project.sh'));
50
+ });
51
+
52
+ test('a builder label names the site and what is set up, not the runner', () => {
53
+ // Which runner drives it — Playwright underneath webship-js — is an implementation detail of the
54
+ // stack. In the label it dates the entry and makes two builders that do the same thing look like
55
+ // two different things. The stack itself (webship-js) is what is set up, so it may be named.
56
+ const { ROOT } = require('../../workspaces');
57
+ const dir = path.join(ROOT, 'test');
58
+ if (!fs.existsSync(dir)) return;
59
+ for (const f of findBuilderScripts(dir)) {
60
+ const label = builderLabel(dir, f);
61
+ assert.ok(!/playwright|cypress|nightwatch/i.test(label), `${f} names its runner in the label: ${label}`);
62
+ if (f.startsWith('cmd-automated-testing-')) {
63
+ assert.match(label, /\+ Automated Testing\b/, `${f} should say what it sets up`);
64
+ }
65
+ }
66
+ });
67
+
68
+ test('a builder is labelled by its own header, not by its filename', () => {
69
+ const dir = folder({
70
+ 'cmd-automated-testing-webship11-0-x-project.sh': '#!/usr/bin/env bash\n\n# workspace-name: Webship 11.0.x + Automated Testing (webship-js)\n',
71
+ 'cmd-nameless-project.sh': '#!/usr/bin/env bash\n',
72
+ });
73
+ assert.equal(builderLabel(dir, 'cmd-automated-testing-webship11-0-x-project.sh'),
74
+ 'Webship 11.0.x + Automated Testing (webship-js)');
75
+ // No header is not an error — the filename is a worse name, not no name.
76
+ assert.equal(builderLabel(dir, 'cmd-nameless-project.sh'), 'cmd-nameless-project.sh');
77
+ });
78
+
79
+ test('a pinned builder says the release it installs, and pairs with a branch one', () => {
80
+ // `11-0-x` is the branch (11.0.x-dev) and `11-0-0` is the released tag (~11.0.0). The label says
81
+ // "Webship 11.0.0" beside "Webship 11.0.x": the last digit is the whole difference between a tag
82
+ // and a branch, and it must be in the label. Asserted against the tree, in every workspace that builds.
83
+ const { ROOT } = require('../../workspaces');
84
+ for (const ws of ['dev', 'test', 'demos', 'sandboxes']) {
85
+ const dir = path.join(ROOT, ws);
86
+ if (!fs.existsSync(dir)) continue;
87
+ for (const f of findBuilderScripts(dir)) {
88
+ const twin = /-(\d+)-(\d+)-0-/.exec(f);
89
+ if (!twin) continue;
90
+ const body = fs.readFileSync(path.join(dir, f), 'utf8');
91
+ assert.match(body, /^(site_version|[a-z]+_version|site_template_version_default)="~/m,
92
+ `${ws}/${f} is named as a release but does not pin one`);
93
+ assert.ok(builderLabel(dir, f).includes(`${twin[1]}.${twin[2]}.0`),
94
+ `${ws}/${f} installs ${twin[1]}.${twin[2]}.0 but its label does not say so: ${builderLabel(dir, f)}`);
95
+ // Every pinned builder is the twin of a branch one, never an orphan.
96
+ assert.ok(fs.existsSync(path.join(dir, f.replace(/-(\d+)-(\d+)-0-/, '-$1-$2-x-'))),
97
+ `${ws}/${f} has no branch twin`);
98
+ }
99
+ }
100
+ });
101
+
102
+ test('the test workspace really does offer every builder it ships', () => {
103
+ // Against the tree itself: the bug was a filter that looked right in isolation.
104
+ const dir = path.join(require('../../workspaces').ROOT, 'test');
105
+ if (!fs.existsSync(dir)) return; // a clone without the workspaces built
106
+ const onDisk = fs.readdirSync(dir)
107
+ .filter((f) => /^cmd-.*-project\.sh$/.test(f) && !/^cmd-tools?-/.test(f)).sort();
108
+ // Compared as a SET: the list is no longer alphabetical — BUILDER_ORDER puts the current
109
+ // Drupal, the next one, Webship and the site templates at the head — and what this test is
110
+ // about is that nothing is missing, not what order it comes in.
111
+ assert.deepEqual([...findBuilderScripts(dir)].sort(), onDisk);
112
+ assert.ok(onDisk.some((f) => f.startsWith('cmd-automated-testing-')), 'including the testing ones');
113
+ });
114
+
115
+ test('the builders people reach for are at the head of the list, in order', () => {
116
+ // Alphabetical put the `cmd-automated-testing-…` builds above everything in the test workspace,
117
+ // and `cmd-cucumber11-…` above Drupal and Webship everywhere else.
118
+ const dir = folder({
119
+ 'cmd-automated-testing-webship11-0-x-project.sh': '#!/usr/bin/env bash\n',
120
+ 'cmd-drupal9-recommended-project.sh': '#!/usr/bin/env bash\n',
121
+ 'cmd-website_starter-project.sh': '#!/usr/bin/env bash\n',
122
+ 'cmd-cucumber11-0-x-project.sh': '#!/usr/bin/env bash\n',
123
+ 'cmd-webships2-0-x-project.sh': '#!/usr/bin/env bash\n',
124
+ 'cmd-webship11-0-0-project.sh': '#!/usr/bin/env bash\n',
125
+ 'cmd-webship11-0-x-project.sh': '#!/usr/bin/env bash\n',
126
+ 'cmd-drupalcms2-x-project.sh': '#!/usr/bin/env bash\n',
127
+ 'cmd-drupal12-recommended-project.sh': '#!/usr/bin/env bash\n',
128
+ 'cmd-drupal11-recommended-project.sh': '#!/usr/bin/env bash\n',
129
+ });
130
+ assert.deepEqual(findBuilderScripts(dir).slice(0, 8), [
131
+ // The three bases, then Webship — the branch and its pinned twin together, the alphabet between them.
132
+ 'cmd-drupal11-recommended-project.sh',
133
+ 'cmd-drupal12-recommended-project.sh',
134
+ 'cmd-drupalcms2-x-project.sh',
135
+ 'cmd-webship11-0-0-project.sh',
136
+ 'cmd-webship11-0-x-project.sh',
137
+ 'cmd-webships2-0-x-project.sh',
138
+ 'cmd-cucumber11-0-x-project.sh',
139
+ 'cmd-website_starter-project.sh',
140
+ ]);
141
+ // Everything unnamed keeps its alphabetical place below, so a new builder needs no code
142
+ // change unless it belongs at the top.
143
+ assert.deepEqual(findBuilderScripts(dir).slice(8), [
144
+ 'cmd-automated-testing-webship11-0-x-project.sh',
145
+ 'cmd-drupal9-recommended-project.sh',
146
+ ]);
147
+ });
148
+
149
+ test('every workspace that builds sites offers Drupal 12', () => {
150
+ const { ROOT } = require('../../workspaces');
151
+ for (const ws of ['dev', 'test', 'demos', 'sandboxes', 'projects']) {
152
+ const dir = path.join(ROOT, ws);
153
+ if (!fs.existsSync(dir)) continue; // a clone without the workspaces built
154
+ const found = findBuilderScripts(dir);
155
+ assert.ok(found.includes('cmd-drupal12-recommended-project.sh'), `${ws} has the Drupal 12 builder`);
156
+ assert.equal(builderLabel(dir, 'cmd-drupal12-recommended-project.sh'), 'Drupal 12 (latest)');
157
+ }
158
+ });
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+ const test = require('node:test');
3
+ const assert = require('node:assert');
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+
7
+ const server = fs.readFileSync(path.join(__dirname, '../../server.js'), 'utf8');
8
+ const css = fs.readFileSync(path.join(__dirname, '../../public/css/components/project-cards.css'), 'utf8');
9
+
10
+ test('the state chip on a card switches the project: Stopped and Paused start it, Running stops it', () => {
11
+ assert.match(server, /const act = st === 'running' \? 'Stop' : 'Start';/);
12
+ assert.match(server, /hx-post="\/actions\/ddev-\$\{act\.toLowerCase\(\)\}"/);
13
+ assert.match(server, /<button type="button" class="uk-label \$\{cls\} state-chip state-toggle/);
14
+ });
15
+
16
+ test('stopping from the chip goes through the confirm dialog; starting does not', () => {
17
+ assert.match(server, /state-toggle\$\{st === 'running' \? ' arm-step' : ''\}/);
18
+ assert.match(server, /\$\{st === 'running' \? ' hx-trigger="confirmed-remove"' : ''\}/);
19
+ });
20
+
21
+ test('a DDEV state other than these three is shown, not offered as a switch', () => {
22
+ assert.match(server, /if \(!st\) return `<span class="uk-label state-chip state-chip-off">/);
23
+ });
24
+
25
+ test('hovering the chip says what a click does, and Paused has its own colour and stripe', () => {
26
+ assert.match(css, /\.state-toggle:is\(:hover, :focus-visible\) \.state-act \{ visibility: visible; \}/);
27
+ assert.match(css, /\.state-chip-paused \{ background: var\(--warning-soft\); color: var\(--warning-ink\);/);
28
+ assert.match(css, /\.project-row\.pcard:has\(\.state-chip-paused\) \{ --pcard-stripe: var\(--warning\); \}/);
29
+ });
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Reading the changelogs in products/.
3
+ *
4
+ * The parser's job is to be right about files nobody wrote for it. Three formats are in the tree
5
+ * and each is tested on its own shape, because a reader that works for the products somebody
6
+ * remembered is a reader people stop trusting the first time a version is missing.
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const test = require('node:test');
12
+ const assert = require('node:assert');
13
+
14
+ const {
15
+ parseChangelog, kindOf, looksLikeVersion, listChangelogs, changelogFor, releasesFor, changelogIndex,
16
+ } = require('../../changelog');
17
+
18
+ /* ---------------- the formats ---------------- */
19
+
20
+ test('Keep a Changelog: version, date, and the kind from the section', () => {
21
+ const [rel] = parseChangelog(`# Changelog
22
+
23
+ ## [1.0.3] - 2026-09-14
24
+ ### Added
25
+ - Add a Newsletter Canvas page at \`/newsletter\`
26
+
27
+ ### Fixed
28
+ - Give the footer Subscribe button a link destination
29
+ `);
30
+ assert.equal(rel.version, '1.0.3');
31
+ assert.equal(rel.date, '2026-09-14');
32
+ assert.deepEqual(rel.entries.map((e) => e.kind), ['Added', 'Fixed']);
33
+ assert.deepEqual(rel.entries.map((e) => e.tone), ['success', 'danger']);
34
+ });
35
+
36
+ // The bug this was written for: the date separator used to match any dash, so `# 11.0.0-rc1` parsed
37
+ // as version `11.0.0` with `rc1` where the date goes — and a file with six releases showed three of
38
+ // them called 11.0.0. The separator has to be surrounded by whitespace.
39
+ test('a version with a suffix keeps its suffix', () => {
40
+ const releases = parseChangelog(`# 11.0.0
41
+ - task: something
42
+
43
+ # 11.0.0-rc1
44
+ - task: something else
45
+
46
+ # 11.0.0-beta2
47
+ - task: a third thing
48
+ `);
49
+ assert.deepEqual(releases.map((r) => r.version), ['11.0.0', '11.0.0-rc1', '11.0.0-beta2']);
50
+ assert.deepEqual(releases.map((r) => r.date), ['', '', '']);
51
+ });
52
+
53
+ test('the old Drupal .txt: a title line underlined with dashes', () => {
54
+ const releases = parseChangelog(`Menu Position 7.x-1.1, 2012-02-22
55
+ ---------------------------------
56
+ - #1439948 by reglogge: form validation fails
57
+
58
+ Menu Position 7.x-1.0, 2011-11-01
59
+ ---------------------------------
60
+ - by davereid: rowCount does not work on sqlite
61
+ `);
62
+ assert.equal(releases.length, 2);
63
+ assert.equal(releases[0].version, 'Menu Position 7.x-1.1');
64
+ assert.equal(releases[0].date, '2012-02-22');
65
+ assert.equal(releases[1].date, '2011-11-01');
66
+ });
67
+
68
+ test('a prefix on the entry beats the section it sits under', () => {
69
+ // These files disagree with themselves in practice — a `fix:` often lives under `### Added` —
70
+ // and the prefix is the more specific statement.
71
+ const [rel] = parseChangelog(`## [1.0.0] - 2026-01-01
72
+ ### Added
73
+ - fix: [#1](https://example.com) this is really a fix
74
+ - feat: and this is really a feature
75
+ `);
76
+ assert.deepEqual(rel.entries.map((e) => e.kind), ['Fixed', 'Added']);
77
+ // The prefix is consumed, not left in the text.
78
+ assert.ok(!rel.entries[0].text.startsWith('fix:'), rel.entries[0].text);
79
+ });
80
+
81
+ test('a hard-wrapped entry is one entry, not four', () => {
82
+ const [rel] = parseChangelog(`## [1.0.0] - 2026-01-01
83
+ ### Fixed
84
+ - fix: a finding that runs
85
+ across three
86
+ separate lines
87
+ `);
88
+ assert.equal(rel.entries.length, 1);
89
+ assert.match(rel.entries[0].text, /runs across three separate lines/);
90
+ });
91
+
92
+ test('the Webship shape: a release is grouped by package', () => {
93
+ const [rel] = parseChangelog(`# 11.0.0
94
+ ### At a glance
95
+ * The suite reaches its first stable release.
96
+
97
+ #### Website Starter 1.0.1
98
+ - fix: something in the starter
99
+
100
+ #### Website Starter 1.0.1
101
+ - feat: something in website_starter
102
+ `);
103
+ assert.equal(rel.version, '11.0.0');
104
+ assert.deepEqual(rel.entries.map((e) => e.group),
105
+ ['At a glance', 'Website Starter 1.0.1', 'Website Starter 1.0.1']);
106
+ });
107
+
108
+ test('markdown is kept, because the reader must not show less than the file', () => {
109
+ const [rel] = parseChangelog(`## [1.0.0] - 2026-01-01
110
+ - fix: [#3622817](https://www.drupal.org/i/3622817) a thing in \`config/optional\`
111
+ `);
112
+ assert.match(rel.entries[0].text, /\[#3622817\]\(https:\/\/www\.drupal\.org\/i\/3622817\)/);
113
+ assert.match(rel.entries[0].text, /`config\/optional`/);
114
+ });
115
+
116
+ /* ---------------- the small pieces ---------------- */
117
+
118
+ test('a kind maps to a label and a UIkit tone, whichever vocabulary it is written in', () => {
119
+ assert.deepEqual(kindOf('Added'), { label: 'Added', tone: 'success' });
120
+ assert.deepEqual(kindOf('feat'), { label: 'Added', tone: 'success' });
121
+ assert.deepEqual(kindOf('fix'), { label: 'Fixed', tone: 'danger' });
122
+ assert.deepEqual(kindOf('Security'), { label: 'Security', tone: 'danger' });
123
+ assert.equal(kindOf('At a glance'), null, 'prose is not a kind');
124
+ assert.equal(kindOf(''), null);
125
+ });
126
+
127
+ test('a heading is a version or it is prose', () => {
128
+ for (const v of ['1.0.3', '11.0.0-rc1', '8.x-1.1', 'Unreleased', 'v2.3']) {
129
+ assert.ok(looksLikeVersion(v), `${v} is a version`);
130
+ }
131
+ for (const p of ['At a glance', 'Changelog', 'Stability of Webship 11.0.0', 'Added']) {
132
+ assert.ok(!looksLikeVersion(p), `${p} is not a version`);
133
+ }
134
+ });
135
+
136
+ test('an empty file, and a file of nothing but prose, are no releases rather than a crash', () => {
137
+ assert.deepEqual(parseChangelog(''), []);
138
+ assert.deepEqual(parseChangelog('# Changelog\n\nAll notable changes are documented here.\n'), []);
139
+ });
140
+
141
+ /* ---------------- against the real tree ---------------- */
142
+
143
+ test('a changelog anywhere in the tree is found, not only in products/', () => {
144
+ const found = listChangelogs();
145
+ assert.ok(found.length > 0, 'nothing in the tree keeps a changelog');
146
+ for (const c of found) {
147
+ assert.match(c.file, /^CHANGELOG\.(md|txt)$/i);
148
+ assert.ok(c.bytes > 0);
149
+ assert.ok(c.workspace, 'every entry names the workspace it was found in');
150
+ }
151
+ // The point of the change: it is a walk of every workspace, not a list of one.
152
+ const workspaces = new Set(found.map((c) => c.workspace));
153
+ assert.ok(workspaces.size > 1,
154
+ `only ${[...workspaces]} was walked — a built site in dev/ keeps one too`);
155
+ });
156
+
157
+ test('a name that climbs out of its workspace is refused', () => {
158
+ for (const bad of ['../etc', '..', 'a/b', 'no-such-item-here']) {
159
+ assert.equal(changelogFor('products', bad), null, `products/${bad} should not resolve`);
160
+ assert.equal(releasesFor('products', bad), null);
161
+ }
162
+ // And a workspace that is not one.
163
+ assert.equal(changelogFor('../../etc', 'passwd'), null);
164
+ assert.equal(changelogFor('no-such-workspace', 'x'), null);
165
+ });
166
+
167
+ test('the index summarises everything without throwing on any of it', () => {
168
+ const rows = changelogIndex();
169
+ assert.equal(rows.length, listChangelogs().length);
170
+ for (const r of rows) {
171
+ assert.equal(typeof r.releases, 'number');
172
+ assert.equal(typeof r.entries, 'number');
173
+ assert.ok(r.workspace);
174
+ // The newest release is the first in the file, never "Unreleased" — a version people can install.
175
+ assert.ok(!/^unreleased$/i.test(r.latest), `${r.label} reports Unreleased as its latest`);
176
+ }
177
+ });
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The assistant's thread: what keeps one conversation across pages.
3
+ *
4
+ * The two things that can quietly break it are both checked here. The id must travel in a cookie
5
+ * scoped to the HUB domain — localStorage, or a cookie on the page's own host, would make /dev and
6
+ * /test two different conversations, which is the bug this exists to fix. And the thread must carry
7
+ * claude's session id, because a transcript redrawn in front of an agent that remembers none of it
8
+ * reads as memory and is not.
9
+ */
10
+
11
+ const assert = require('node:assert');
12
+ const { test } = require('node:test');
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const app = path.join(__dirname, '..', '..');
17
+ const chats = require(path.join(app, 'chat-threads'));
18
+ const { hubDomain } = require(path.join(app, 'workspaces'));
19
+
20
+ const withCookie = (id) => ({ headers: { cookie: `ws-per=50; ws-chat=${id}; ws-theme=dark` } });
21
+
22
+ test('the id comes from the cookie, and only in our own shape', () => {
23
+ const id = chats.newId();
24
+ assert.match(id, /^[a-f0-9]{32}$/);
25
+ assert.strictEqual(chats.threadIdFrom(withCookie(id)), id);
26
+ assert.strictEqual(chats.threadIdFrom({ headers: {} }), null);
27
+ // The id names a file, so anything that is not the shape we write is not read back.
28
+ for (const bad of ['../../etc/passwd', 'a'.repeat(31), 'A'.repeat(32), '']) {
29
+ assert.strictEqual(chats.threadIdFrom(withCookie(bad)), null, `${bad} was accepted`);
30
+ }
31
+ });
32
+
33
+ test('the cookie is scoped to the hub domain, so every workspace subdomain is one conversation', () => {
34
+ const c = chats.threadCookie(chats.newId());
35
+ assert.match(c, new RegExp(`Domain=\\.${hubDomain().replace(/\./g, '\\.')}`), 'not scoped to the hub');
36
+ assert.match(c, /Path=\//);
37
+ assert.match(c, /SameSite=Lax/);
38
+ // No Max-Age on purpose: the conversation belongs to this browsing session.
39
+ assert.ok(!/Max-Age/.test(c), 'the chat cookie must not outlive the browser');
40
+ });
41
+
42
+ test('a thread keeps the turns and the claude session, and reads back the same', () => {
43
+ let t = chats.loadThread(withCookie(chats.newId()));
44
+ assert.deepEqual(t.turns, []);
45
+ assert.strictEqual(t.sessionId, null);
46
+ t = chats.appendTurn(t, { role: 'user', text: 'what is running?' });
47
+ t = chats.appendTurn({ ...t, sessionId: 'abc-123' }, { role: 'ai', html: '<p>Two projects.</p>' });
48
+ try {
49
+ const back = chats.readThread(t.id);
50
+ assert.strictEqual(back.sessionId, 'abc-123', 'the session to resume must survive');
51
+ assert.deepEqual(chats.historyFor(back), [
52
+ { role: 'user', text: 'what is running?' },
53
+ { role: 'ai', html: '<p>Two projects.</p>' },
54
+ ], 'the history must be deep-chat’s own shape');
55
+ // Written 0600: it is a chat log, and it says so on disk.
56
+ assert.strictEqual(fs.statSync(path.join(chats.DIR, `${t.id}.json`)).mode & 0o777, 0o600);
57
+ } finally {
58
+ chats.clearThread(t.id);
59
+ }
60
+ assert.strictEqual(chats.readThread(t.id), null, 'clearing must remove the file');
61
+ });
62
+
63
+ test('a long conversation is trimmed from the front, not refused', () => {
64
+ let t = chats.loadThread({ headers: {} });
65
+ for (let i = 0; i < chats.MAX_TURNS + 10; i += 1) t = chats.appendTurn(t, { role: 'user', text: `turn ${i}` });
66
+ try {
67
+ assert.strictEqual(t.turns.length, chats.MAX_TURNS);
68
+ assert.strictEqual(t.turns[t.turns.length - 1].text, `turn ${chats.MAX_TURNS + 9}`, 'the newest turn must be kept');
69
+ } finally {
70
+ chats.clearThread(t.id);
71
+ }
72
+ });
73
+
74
+ test('the assistant resumes its session instead of starting over', () => {
75
+ const src = fs.readFileSync(path.join(app, 'assistant.js'), 'utf8');
76
+ assert.ok(!/'--no-session-persistence'/.test(src),
77
+ 'that flag is what threw the conversation away; it must not come back');
78
+ assert.match(src, /'--session-id'/, 'the first turn names the session');
79
+ assert.match(src, /'--resume'/, 'every later turn resumes it');
80
+ assert.match(src, /if \(parsed\.session_id\) newSessionId/, 'what to resume next is read back from the run');
81
+ assert.match(src, /if \(sessionId && result\.code !== 0\)/,
82
+ 'a session that is gone must still answer the question, on a fresh one');
83
+ });
84
+
85
+ test('the chat store is never committed and never synced', () => {
86
+ const ignore = fs.readFileSync(path.join(app, '..', '..', '.gitignore'), 'utf8');
87
+ assert.match(ignore, /core\/workspace-app\/\.chats\//, 'the chat log must be gitignored');
88
+ for (const f of ['scripts/update.sh', path.join('..', 'scripts', 'sync-tooling.sh')]) {
89
+ const sh = fs.readFileSync(path.join(app, f), 'utf8');
90
+ assert.match(sh, /--exclude=\.chats/, `${f} would overwrite the conversations`);
91
+ }
92
+ });