@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
@@ -58,7 +58,6 @@ const BUILTIN_BUTTONS = [
58
58
  // Single buttons, with no menu inside to arrange or to add actions to.
59
59
  { id: 'backup', label: 'Backup', row: 1, menu: false },
60
60
  { id: 'clone', label: 'Clone', row: 1, menu: false },
61
- { id: 'graph', label: 'Graph', row: 1 },
62
61
  { id: 'rag', label: 'RAG', row: 1 },
63
62
  { id: 'more', label: 'More', row: 1 },
64
63
  ];
@@ -26,7 +26,6 @@ const {
26
26
  workspaceDir,
27
27
  findBackupScript,
28
28
  findRemoveScript,
29
- findGraphifyScript,
30
29
  findRagifyScript,
31
30
  findBuilderScripts,
32
31
  listBackups,
@@ -85,7 +84,7 @@ const {
85
84
  // Optional features workspaces turn on from settings.yml `plugins:` — see PLUGINS.md.
86
85
  const {
87
86
  pluginActions, pluginFragment, pluginPage, pluginPageActionGroups, pluginProjectActionsHtml,
88
- pluginRowMenus, pluginOpenWith, pluginFolderLinks, pluginProjectChipsHtml, pluginLabels,
87
+ pluginRowMenus, pluginWorkspaceView, pluginOpenWith, pluginFolderLinks, pluginProjectChipsHtml, pluginLabels,
89
88
  pluginProjectBadgesHtml, pluginSettingsOptions, pluginWorkKeyLink, pluginWorkPrompt,
90
89
  } = require('./plugin-loader');
91
90
  // How a project row is arranged — the order of its buttons and of what is inside their menus (Commands → Buttons).
@@ -133,7 +132,6 @@ const {
133
132
  const { commandAuthorPrompt, commandEditPrompt } = require('./command-author');
134
133
  const { checksCaption, checksDeclaredOf, checksRowsOf } = require('./checks');
135
134
  const { testReportPdfHtml } = require('./report-pdf');
136
- const { graphStoreDir, hasGraph, graphCounts, listGraphs } = require('./graphs');
137
135
  const {
138
136
  milvusProjectName, milvusRestBase, ragInstances, ragBindings, ragInstanceFor, ragWriteBinding,
139
137
  ragCollectionName, milvusPost, ragCollections, ragInvalidate,
@@ -143,7 +141,6 @@ const {
143
141
  ddevProjectName, listProjects, listProjectsDetailed,
144
142
  startInScreen, screenStartedHtml, screenAction, screenSessionName,
145
143
  } = require('./jobs');
146
- const { vaultDir, indexNote, vaultView, vaultCanvas, vaultFrame, obsidianMenuItems } = require('./obsidian');
147
144
  const { assembleCss, cssVersion, listThemes, themeLabel, createTheme, themePreview, isYamlOnlyTheme } = require('./themes');
148
145
  const NAME_RE = /^[a-zA-Z0-9_\-]+$/;
149
146
 
@@ -2887,7 +2884,8 @@ function homeCardsHtml(message) {
2887
2884
  const workspaces = loadWorkspaces();
2888
2885
  const keys = Object.keys(workspaces);
2889
2886
  const cards = Object.values(workspaces).map((w) => {
2890
- const projectCount = w.key === 'graphs' ? listGraphs().length
2887
+ const view = pluginWorkspaceView(w.key);
2888
+ const projectCount = view && view.count ? view.count()
2891
2889
  : w.kind === 'files' ? listItems(w.key).length : listProjects(w.dir).length;
2892
2890
  const hasBuilders = w.kind === 'files' || !w.typeActions.has('build') ? false : findBuilderScripts(w.dir).length > 0;
2893
2891
  const backupCount = listBackups(w.key).length;
@@ -2922,6 +2920,24 @@ function homeCardsHtml(message) {
2922
2920
  uk-grid data-sortable=".ws-card-wrap" data-sortable-view="home"
2923
2921
  data-order="${esc(keys.join(','))}" data-file="settings.yml" data-key="workspaces">${cards}</div>
2924
2922
  ${message || ''}
2923
+ ${keys.length ? '' : emptyWorkspaceHtml()}
2924
+ </div>`;
2925
+ }
2926
+
2927
+ // A fresh workspace has no folders and no plugins: that is the design, not a fault, and the first
2928
+ // thing to see is what can be added and how.
2929
+ function emptyWorkspaceHtml() {
2930
+ const { FAMILIES } = require('./plugin-catalog');
2931
+ const families = FAMILIES.map((f) => `<li><strong>${esc(f.label)}</strong> <span class="uk-text-meta">${esc(f.plugins.map(([n]) => n).join(' · '))}</span></li>`).join('');
2932
+ return `<div class="uk-card uk-card-default uk-card-body uk-margin-small-top empty-workspace">
2933
+ <h2 class="uk-h4">Empty, on purpose</h2>
2934
+ <p>This workspace has no folders and no plugins yet. Everything is an npm package <code>@webship.co/workspace-&lt;name&gt;</code>,
2935
+ added one at a time when you ask for it — from <a href="/settings/plugins">Settings → Plugins</a>, or:</p>
2936
+ <pre class="uk-background-muted uk-padding-small">webship-workspace plugin require dev
2937
+ webship-workspace plugin activate dev
2938
+ webship-workspace plugin install dev</pre>
2939
+ <ul class="uk-list uk-list-collapse">${families}</ul>
2940
+ <p class="uk-text-meta"><a href="/settings/plugins">Open the catalogue</a> · the full list: <code>webship-workspace catalog</code></p>
2925
2941
  </div>`;
2926
2942
  }
2927
2943
 
@@ -3144,7 +3160,6 @@ async function projectRowsHtml(key, dir, state) {
3144
3160
  const canBackup = allowed.has('backup') && !!findBackupScript(dir);
3145
3161
  const canClone = allowed.has('clone') && fs.existsSync(path.join(dir, 'cmd-tools-clone.sh'));
3146
3162
  const canRemove = allowed.has('remove') && !!findRemoveScript(dir);
3147
- const canGraphify = allowed.has('graphify') && !!findGraphifyScript(dir);
3148
3163
  const canRagify = allowed.has('ragify') && !!findRagifyScript(dir);
3149
3164
  const canTest = allowed.has('tests') && fs.existsSync(path.join(dir, 'cmd-tools-testing.sh'));
3150
3165
  const canChecks = allowed.has('checks') && fs.existsSync(path.join(dir, 'cmd-tools-checks.sh'));
@@ -3459,25 +3474,6 @@ async function projectRowsHtml(key, dir, state) {
3459
3474
  hx-get="/fragments/projects/clone-form/${encodeURIComponent(key)}/${encodeURIComponent(p)}" hx-target="#editor-modal-body" hx-swap="innerHTML"
3460
3475
  hx-on::after:request="UIkit.modal('#editor-modal').show()"
3461
3476
  title="Clone this ${esc(meta.noun)} — a copy, or a git worktree sharing its history"><span uk-icon="icon: copy; ratio: .7"></span> Clone</button>` : '';
3462
- const graphMenu = canGraphify ? (() => {
3463
- const built = hasGraph(key, p, 'graph.json') || hasGraph(key, p, 'graph.html');
3464
- const busy = !!runningJobKey(`graphify:${key}/${p}`);
3465
- return `<span class="act-with-status"><div class="uk-inline act-menu act-menu-graph">
3466
- <button class="uk-button uk-button-default uk-button-small" type="button" title="Knowledge graph"><span uk-icon="icon: git-fork; ratio: .7"></span> Graph <span uk-icon="icon: chevron-down; ratio: .6"></span></button>
3467
-
3468
- <div uk-dropdown="mode: click; pos: bottom-right"><ul class="uk-nav uk-dropdown-nav">
3469
- <li data-entry="build the graph"><a href hx-post="/actions/graphify" ${vals()} class="${busy ? 'uk-disabled' : ''}"><span uk-icon="icon: play; ratio: .7"></span> ${built ? 'Rebuild the graph' : 'Build the graph'}${busy ? ' (running…)' : ''}</a></li>
3470
- ${built ? `<li class="uk-nav-divider"></li>
3471
- ${hasGraph(key, p, 'graph.html') ? `<li><a href="/graph/${esc(key)}/${esc(p)}" target="_blank" title="Loads vis-network from unpkg.com, so it needs internet"><span uk-icon="icon: image; ratio: .7"></span> Open the graph</a></li>` : ''}
3472
- ${hasGraph(key, p, 'GRAPH_REPORT.md') ? `<li><a href="/graph/${esc(key)}/${esc(p)}/report" target="_blank"><span uk-icon="icon: file-text; ratio: .7"></span> Read the report</a></li>` : ''}
3473
- <li><a href="/graph/${esc(key)}/${esc(p)}/download" title="The graph as a .tar.gz, to hand to another workspace"><span uk-icon="icon: download; ratio: .7"></span> Download to share</a></li>
3474
- <li><a href hx-post="/actions/graph-mcp-command" ${vals()}><span uk-icon="icon: link; ratio: .7"></span> Connect over MCP…</a></li>
3475
- ${obsidianMenuItems(key, p, vals, busy)}
3476
- <li class="uk-nav-divider"></li>
3477
- <li><a href class="uk-text-danger arm-step" hx-post="/actions/graph-remove" ${vals(',"confirm":"yes"')} hx-trigger="confirmed-remove"><span uk-icon="icon: trash; ratio: .7"></span> Delete the graph</a></li>` : ''}
3478
- </ul></div>
3479
- </div><span class="act-status uk-text-meta">${built ? 'built' : 'none'}</span></span>`;
3480
- })() : '';
3481
3477
  const ragMenu = canRagify ? (() => {
3482
3478
  // The RAG index: the same shape as the Graph menu next to it, because it answers the
3483
3479
  // half of the tree the graph does not — prose, templates and configuration.
@@ -3559,7 +3555,7 @@ async function projectRowsHtml(key, dir, state) {
3559
3555
  // The menus activated plugins bring, in the slots they registered (UPSUN, …).
3560
3556
  ...pluginRowMenus({ key, project: p, dir, meta, allowed, vals }),
3561
3557
  screens: screensOnTools, composer: composerMenu, node: nodeMenu, backup: backupButton, clone: cloneButton,
3562
- graph: graphMenu, rag: ragMenu, more: moreMenu,
3558
+ rag: ragMenu, more: moreMenu,
3563
3559
  }, rowCtx, { preArranged: new Set(['ddev', 'more']) });
3564
3560
  // What is written down about this folder, read once: the name it is called by is the heading, the
3565
3561
  // paragraph is the row's tooltip, and the pen that edits both sits in the card's own corner.
@@ -3840,7 +3836,7 @@ function pageActionGroups(context) {
3840
3836
  groups.push({ id: 'go-to', label: 'Go to', items: [
3841
3837
  link('default', 'arrow-left', `Back to ${meta.label}`, wsUrl(key)),
3842
3838
  ] });
3843
- } else if (meta && meta.kind === 'files' && key !== 'graphs') {
3839
+ } else if (meta && meta.kind === 'files' && !(pluginWorkspaceView(key) || {}).noFilesActions) {
3844
3840
  // Audio makes things rather than writes them, so each kind of thing is its own action and its own modal.
3845
3841
  // Each is a modal, not an inline form: the result of generating stays in the window that asked for it.
3846
3842
  const modal = 'hx-target="#editor-modal-body" hx-swap="innerHTML"';
@@ -4324,57 +4320,6 @@ function commandsShell(title, subtitle, active, body, context = 'commands') {
4324
4320
  </main>`, context);
4325
4321
  }
4326
4322
 
4327
- function graphRowsHtml(state) {
4328
- const all = listGraphs();
4329
- const st = state;
4330
- const filtered = st.q
4331
- ? all.filter((g) => g.name.toLowerCase().includes(st.q.toLowerCase()))
4332
- : all;
4333
- const sorted = filtered.slice().sort((SORTS[st.sort] || SORTS[defaultSort()] || SORTS.newest).cmp);
4334
- const p = paginate(sorted, st);
4335
-
4336
- const rows = p.slice.map((g) => `
4337
- <div class="uk-card uk-card-default uk-card-small uk-card-body uk-margin-small project-row">
4338
- <div class="item-row-body">
4339
- <span class="uk-text-bold">${iconHtml('tabler:hierarchy-2', 0.8)} ${esc(g.project)}
4340
- <span class="uk-text-meta item-machine" title="The workspace the project lives in">${esc(g.workspace)}</span></span>
4341
- <div class="item-meta-line">
4342
- <span class="uk-text-meta item-time">${esc(humanTime(g.mtime))}</span>
4343
- <span class="uk-text-meta">· ${esc(humanSize(g.size))}</span>
4344
- ${g.nodes != null ? `<span class="uk-label" title="Nodes and edges, from graphify's own build file">${g.nodes} nodes${g.links != null ? ` · ${g.links} edges` : ''}</span>` : ''}
4345
- ${g.projectExists ? '' : '<span class="uk-label" title="The project is gone — the graph outlived it, and is safe to delete">project removed</span>'}
4346
- </div>
4347
- <div class="project-actions">
4348
- ${g.hasHtml ? `<a class="uk-button uk-button-default uk-button-small" href="/graph/${esc(g.workspace)}/${esc(g.project)}" target="_blank" title="Loads vis-network from unpkg.com, so it needs internet"><span uk-icon="icon: image; ratio: .7"></span> Open</a>` : ''}
4349
- ${g.hasReport ? `<a class="uk-button uk-button-default uk-button-small" href="/graph/${esc(g.workspace)}/${esc(g.project)}/report" target="_blank"><span uk-icon="icon: file-text; ratio: .7"></span> Report</a>` : ''}
4350
- <a class="uk-button uk-button-default uk-button-small" href="/graph/${esc(g.workspace)}/${esc(g.project)}/download"><span uk-icon="icon: download; ratio: .7"></span> Download</a>
4351
- ${g.projectExists ? `<button class="uk-button uk-button-secondary uk-button-small" hx-post="/actions/graphify" hx-vals='{"workspace":"${esc(g.workspace)}","projectName":"${esc(g.project)}"}' hx-target="#webship-workspace-output" hx-swap="innerHTML" title="Re-extract and re-cluster from the project"><span uk-icon="icon: refresh; ratio: .7"></span> Rebuild</button>` : ''}
4352
- <button class="uk-button uk-button-danger uk-button-small arm-step" hx-post="/actions/graph-remove" hx-vals='{"workspace":"${esc(g.workspace)}","projectName":"${esc(g.project)}","confirm":"yes"}' hx-target="#webship-workspace-output" hx-swap="innerHTML" hx-trigger="confirmed-remove"><span uk-icon="icon: trash; ratio: .7"></span> Delete</button>
4353
- </div>
4354
- </div>
4355
- </div>`).join('');
4356
-
4357
- // The controls get the BARE URL: htmx appends the included inputs without deduping, and
4358
- // readPageState reads the FIRST q/sort — a URL already carrying ?q= made the search box a no-op.
4359
- // The pager keeps the qs form so page links preserve the current search.
4360
- const bareUrl = '/fragments/graphs/list';
4361
- const qs = new URLSearchParams();
4362
- if (st.q) qs.set('q', st.q);
4363
- if (st.sort !== defaultSort()) qs.set('sort', st.sort);
4364
- const url = `${bareUrl}${qs.toString() ? `?${qs}` : ''}`;
4365
- const pager = listPagerHtml(url, '#webship-workspace-projects', p, st, 'graphs');
4366
-
4367
- return `
4368
- <h3 class="uk-margin-small-bottom">Graphs <span class="uk-badge">${all.length}</span></h3>
4369
- <p class="uk-text-meta uk-margin-small-bottom">One directory per project in <code>${esc(path.join(ROOT, 'graphs'))}/&lt;workspace&gt;/&lt;project&gt;/</code>,
4370
- built by <code>cmd-tools-graphify.sh</code> from the project's workspace folder. Query one with
4371
- <code>graphify query "…" --graph ${esc(path.join(ROOT, 'graphs'))}/&lt;workspace&gt;/&lt;project&gt;/graph.json</code>.</p>
4372
- ${listControlsHtml(bareUrl, '#webship-workspace-projects', st, 'graphs')}
4373
- ${pager}
4374
- ${rows || '<p class="uk-text-meta">No graphs yet — build one from the Graph menu on any project row.</p>'}
4375
- ${pager}`;
4376
- }
4377
-
4378
4323
  async function workspacePage(key, state) {
4379
4324
  await refreshLiveSessions();
4380
4325
  const workspaces = loadWorkspaces();
@@ -4384,12 +4329,8 @@ async function workspacePage(key, state) {
4384
4329
  const builders = isFiles || !meta.typeActions.has('build') ? [] : findBuilderScripts(dir);
4385
4330
  const builderOptions = builders.map((s) => `<option value="${esc(s)}">${esc(builderLabel(dir, s))}</option>`).join('');
4386
4331
 
4387
- const listSection = key === 'graphs' ? `
4388
- <div id="webship-workspace-projects" hx-get="/fragments/graphs/list" hx-trigger="refresh-projects from:body">
4389
- ${graphRowsHtml(state)}
4390
- </div>
4391
-
4392
- ` : meta.kind === 'commands' ? `
4332
+ const view = pluginWorkspaceView(key);
4333
+ const listSection = view && view.listHtml ? view.listHtml(state) : meta.kind === 'commands' ? `
4393
4334
  <div id="webship-workspace-projects" hx-get="/fragments/cc/list" hx-trigger="refresh-projects from:body">
4394
4335
  ${await commandCatalogHtml(state)}
4395
4336
  </div>
@@ -4517,8 +4458,6 @@ const ACTIONS = {
4517
4458
  ...require('./actions/ddev'),
4518
4459
  ...require('./actions/ddev-mail').make({ mailpitFor: ddevMail.mailpitFor(ddevStatusMap) }),
4519
4460
  ...require('./actions/ddev-addons'),
4520
- ...require('./actions/graphs'),
4521
- ...require('./actions/obsidian'),
4522
4461
  ...require('./actions/rag'),
4523
4462
  ...require('./actions/testing'),
4524
4463
  ...require('./actions/checks'),
@@ -5660,80 +5599,6 @@ const server = http.createServer(async (req, res) => {
5660
5599
  const out = path.join(tmp, 'report.pdf');
5661
5600
  const cleanup = () => { try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (_) { /* gone */ } };
5662
5601
 
5663
- // The vault: its index note, and the whole thing as an archive. Same symlink discipline
5664
- // as the graph routes below — a project directory that is a link would otherwise read
5665
- // outside the workspace, and this container bind-mounts ~/.claude as well as ~/workspace.
5666
- const vaultMatch = pathname.match(/^\/obsidian\/([a-z0-9_-]+)\/([a-zA-Z0-9_.-]+)\/(index|download|view|canvas|frame)$/);
5667
- if (vaultMatch && isValidWorkspace(vaultMatch[1])) {
5668
- const [, wsKey, project, what] = vaultMatch;
5669
- if (project.includes('..')) { res.writeHead(400); return res.end('bad name'); }
5670
- let real;
5671
- try {
5672
- real = fs.realpathSync(vaultDir(wsKey, project));
5673
- const base = fs.realpathSync(path.join(ROOT, 'graphs'));
5674
- if (!real.startsWith(base + path.sep) || !fs.statSync(real).isDirectory()) throw new Error('outside');
5675
- } catch (_) {
5676
- res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
5677
- return res.end('No vault yet — build one from the Graph menu on the project row.');
5678
- }
5679
-
5680
- if (what === 'frame') {
5681
- const wanted = String(new URL(req.url, 'http://localhost').searchParams.get('what') || 'view');
5682
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
5683
- return res.end(vaultFrame(wsKey, project, wanted));
5684
- }
5685
-
5686
- if (what === 'view' || what === 'canvas') {
5687
- // Built before the head is written: a throw after writeHead becomes a 200 with an
5688
- // empty body, which reads as "the vault is empty" rather than "this broke".
5689
- let page;
5690
- try {
5691
- const themed = cssVersion(styleSettings().theme);
5692
- page = what === 'view'
5693
- ? vaultView(wsKey, project, String(new URL(req.url, 'http://localhost').searchParams.get('note') || ''), themed)
5694
- : vaultCanvas(wsKey, project, themed);
5695
- } catch (e) {
5696
- res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
5697
- return res.end(`Could not read the vault: ${e.message}`);
5698
- }
5699
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
5700
- return res.end(page);
5701
- }
5702
-
5703
- if (what === 'index') {
5704
- // Read before the head is written: computing after writeHead turns a throw into a
5705
- // 200 with an empty body, which reads as "it worked and there is nothing there".
5706
- let md;
5707
- try {
5708
- md = fs.readFileSync(path.join(real, indexNote(project)), 'utf8');
5709
- } catch (_) {
5710
- res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });
5711
- return res.end('<div class="msg error">The vault has no index note. Rebuild it.</div>');
5712
- }
5713
- const vaultUri = `obsidian://open?path=${encodeURIComponent(real)}`;
5714
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
5715
- return res.end(`<h3>${esc(project)} <span class="uk-text-meta">Obsidian vault</span></h3>
5716
- <p class="uk-text-meta">${esc(real)}</p>
5717
- <p><a class="uk-button uk-button-primary uk-button-small" href="${esc(vaultUri)}">Open in Obsidian</a>
5718
- <a class="uk-button uk-button-default uk-button-small" href="/obsidian/${esc(wsKey)}/${esc(project)}/download">Download</a></p>
5719
- <pre class="uk-background-muted uk-padding-small" style="white-space:pre-wrap">${esc(md)}</pre>`);
5720
- }
5721
-
5722
- const vaultArchive = `${wsKey}--${project}--obsidian.tar.gz`;
5723
- res.writeHead(200, {
5724
- 'Content-Type': 'application/gzip',
5725
- 'Content-Disposition': `attachment; filename="${vaultArchive.replace(/[^A-Za-z0-9._-]/g, '_')}"`,
5726
- 'X-Content-Type-Options': 'nosniff',
5727
- });
5728
- const vtar = spawn('tar', ['-czf', '-', '-C', path.dirname(real), path.basename(real)],
5729
- { stdio: ['ignore', 'pipe', 'pipe'] });
5730
- vtar.stdout.pipe(res);
5731
- vtar.stderr.resume();
5732
- vtar.on('error', () => { try { res.destroy(); } catch (_) { /* client gone */ } });
5733
- res.on('close', () => { try { vtar.kill(); } catch (_) { /* already exited */ } });
5734
- return;
5735
- }
5736
-
5737
5602
  // Rendered from the run's JSON through our own print stylesheet, not from
5738
5603
  // cucumber-html-reporter's page: that page's feature and scenario bodies are Bootstrap
5739
5604
  // collapsibles which print CLOSED — a PDF of nothing but headers — and its CSS comes from
@@ -5779,66 +5644,6 @@ const server = http.createServer(async (req, res) => {
5779
5644
  });
5780
5645
  return;
5781
5646
  }
5782
- const graphDlMatch = pathname.match(/^\/graph\/([a-z0-9_-]+)\/([a-zA-Z0-9_.\-]+)\/download$/);
5783
- if (graphDlMatch && isValidWorkspace(graphDlMatch[1])) {
5784
- const [, wsKey, project] = graphDlMatch;
5785
- if (project.includes('..')) { res.writeHead(400); return res.end('bad name'); }
5786
- const out = graphStoreDir(wsKey, project);
5787
- let real;
5788
- try {
5789
- real = fs.realpathSync(out);
5790
- const base = fs.realpathSync(path.join(ROOT, 'graphs'));
5791
- if (!real.startsWith(base + path.sep) || !fs.statSync(real).isDirectory()) throw new Error('nope');
5792
- } catch (_) {
5793
- res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
5794
- return res.end('No graph yet — build one from the Graph menu on the project row.');
5795
- }
5796
- // Streamed straight from tar: a graph runs to tens of MB and buffering it would hold
5797
- // the whole thing in the app's memory.
5798
- const name = `${wsKey}--${project}--graph.tar.gz`;
5799
- res.writeHead(200, {
5800
- 'Content-Type': 'application/gzip',
5801
- 'Content-Disposition': `attachment; filename="${name.replace(/[^A-Za-z0-9._-]/g, '_')}"`,
5802
- 'X-Content-Type-Options': 'nosniff',
5803
- });
5804
- const tar = spawn('tar', ['-czf', '-', '-C', path.dirname(real), path.basename(real)],
5805
- { stdio: ['ignore', 'pipe', 'pipe'] });
5806
- tar.stdout.pipe(res);
5807
- tar.stderr.resume();
5808
- tar.on('error', () => { try { res.destroy(); } catch (_) { /* client gone */ } });
5809
- res.on('close', () => { try { tar.kill(); } catch (_) { /* already exited */ } });
5810
- return;
5811
- }
5812
- const graphMatch = pathname.match(/^\/graph\/([a-z0-9_-]+)\/([a-zA-Z0-9_\-]+)(\/report)?$/);
5813
- if (graphMatch && isValidWorkspace(graphMatch[1])) {
5814
- const wantsReport = !!graphMatch[3];
5815
- const dir = path.join(ROOT, 'graphs');
5816
- const file = path.join(graphStoreDir(graphMatch[1], graphMatch[2]),
5817
- wantsReport ? 'GRAPH_REPORT.md' : 'graph.html');
5818
- // Resolve symlinks before trusting the path: a project directory that
5819
- // is a symlink would otherwise read outside the workspace, and this
5820
- // container bind-mounts ~/.claude as well as ~/workspace.
5821
- let real = null;
5822
- try {
5823
- const base = fs.realpathSync(dir);
5824
- real = fs.realpathSync(file);
5825
- if (real !== base && !real.startsWith(base + path.sep)) real = null;
5826
- if (real && !fs.statSync(real).isFile()) real = null;
5827
- } catch (_) { real = null; }
5828
- if (!real) {
5829
- res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
5830
- return res.end('No graph yet — press Graph on the project row to build one.');
5831
- }
5832
- // graph.html is generated from the project's own code: serve it with
5833
- // scripts sandboxed away from this origin, and stream it (it runs to
5834
- // tens of MB on a large codebase).
5835
- res.writeHead(200, {
5836
- 'Content-Type': wantsReport ? 'text/plain; charset=utf-8' : 'text/html; charset=utf-8',
5837
- 'X-Content-Type-Options': 'nosniff',
5838
- 'Content-Security-Policy': 'sandbox allow-scripts allow-popups;',
5839
- });
5840
- return fs.createReadStream(real).pipe(res);
5841
- }
5842
5647
  const jobMatch = pathname.match(/^\/fragments\/job\/([a-z0-9-]+)$/);
5843
5648
  if (jobMatch) {
5844
5649
  const frag = jobFragment(jobMatch[1]);
@@ -6092,7 +5897,7 @@ const server = http.createServer(async (req, res) => {
6092
5897
  return res.end(made.html);
6093
5898
  }
6094
5899
  const newMatch = pathname.match(/^\/fragments\/([a-z0-9_-]+)\/new$/);
6095
- if (newMatch && newMatch[1] !== 'graphs' && isValidWorkspace(newMatch[1]) && loadWorkspaces()[newMatch[1]].kind === 'files') {
5900
+ if (newMatch && !(pluginWorkspaceView(newMatch[1]) || {}).noNewForm && isValidWorkspace(newMatch[1]) && loadWorkspaces()[newMatch[1]].kind === 'files') {
6096
5901
  const key = newMatch[1];
6097
5902
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
6098
5903
  return res.end(editorFormHtml(key, '', (ITEM_TEMPLATES[key] || ITEM_TEMPLATES.docs)('my-' + loadWorkspaces()[key].noun), true));
@@ -6534,10 +6339,6 @@ const server = http.createServer(async (req, res) => {
6534
6339
  }
6535
6340
  // The images public/ actually holds, as tiles. A settings value has to match a file
6536
6341
  // exactly, so this shows what is there rather than asking you to remember a filename.
6537
- if (pathname === '/fragments/graphs/list') {
6538
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
6539
- return res.end(graphRowsHtml(readPageState(req)));
6540
- }
6541
6342
  if (pathname === '/fragments/jobs/inline') {
6542
6343
  const rows = [...jobs.entries()].filter(([, j]) => !j.done && j.inline).map(([id, j]) => `
6543
6344
  <div class="uk-flex uk-flex-middle uk-flex-between uk-margin-small-bottom">
@@ -0,0 +1,44 @@
1
+ Feature: The actions rail
2
+ The rail on the right of every page collapses to its icons and remembers the choice.
3
+
4
+ # This exists because the feature was broken by having TWO of it. A delegated handler near the
5
+ # top of ui.js toggled the class and wrote `actions-collapsed`; a second implementation at
6
+ # the bottom, from another line of work, bound its own listener to the same button, read the
7
+ # already-toggled class and toggled it straight back. Every click undid itself a millisecond
8
+ # later, so the rail simply did not move. Nothing in the suite noticed, because nothing asserted
9
+ # what a click DOES — only that the button was on the page.
10
+ #
11
+ # So these assert the state after the click, in both directions, and that it is remembered.
12
+
13
+ Scenario: The rail starts collapsed, because the page wants the width more than the labels do
14
+ Given I am on "/dev"
15
+ Then the element "#actions-rail" should be displayed
16
+ And the element ".actions-toggle" should be displayed
17
+ And the element "html" with the attribute "class" and the value containing "actions-collapsed" should exist
18
+ And the element ".actions-toggle" with the attribute "aria-expanded" and the value "false" should exist
19
+
20
+ Scenario: Clicking the toggle expands it
21
+ Given I am on "/dev"
22
+ When I click on the element ".actions-toggle"
23
+ Then the element "html" with the attribute "class" and the value containing "actions-collapsed" should not exist
24
+ And the element ".actions-toggle" with the attribute "aria-expanded" and the value "true" should exist
25
+ And I should see "All workspaces"
26
+
27
+ Scenario: Clicking it again collapses it — the click is not undone by a second handler
28
+ Given I am on "/dev"
29
+ When I click on the element ".actions-toggle"
30
+ And I click on the element ".actions-toggle"
31
+ Then the element "html" with the attribute "class" and the value containing "actions-collapsed" should exist
32
+ And the element ".actions-toggle" with the attribute "aria-expanded" and the value "false" should exist
33
+
34
+ # A layout preference that resets on every navigation is one you set on every page.
35
+ Scenario: The choice survives a navigation
36
+ Given I am on "/dev"
37
+ When I click on the element ".actions-toggle"
38
+ And I am on "/settings"
39
+ Then the element "html" with the attribute "class" and the value containing "actions-collapsed" should not exist
40
+
41
+ Scenario: The rail is on the settings page too, not only on a workspace page
42
+ Given I am on "/settings"
43
+ Then the element "#actions-rail" should be displayed
44
+ And the element ".actions-toggle" should be displayed
@@ -0,0 +1,23 @@
1
+ Feature: Installing agents, skills and prompts
2
+
3
+ Scenario: An item can be installed into the global Claude Code, or into one project
4
+ Given I am on "/ai-agents"
5
+ Then "select[name='toWorkspace']" should be attached
6
+ And "select[name='toProject']" should be attached
7
+ And "[hx-post='/actions/install-item']" should be attached
8
+
9
+ # htmx 4 fires after:swap on the element that MADE the request, not on the one
10
+ # swapped — so a listener reading e.target saw the button and never opened the
11
+ # modal. The content had loaded; nothing appeared.
12
+ Scenario: Editing a prompt opens the editor with its text in it
13
+ Given I am on "/ai-prompts"
14
+ When I click on the element "[hx-get^='/fragments/ai-prompts/edit/'] >> nth=0"
15
+ Then "#editor-modal.uk-open" should be visible
16
+ And "#editor-modal .ace_editor" should be visible
17
+ And "#editor-modal form[hx-post='/actions/save-item']" should be attached
18
+
19
+ Scenario: The same Edit works on the agents workspace
20
+ Given I am on "/ai-agents"
21
+ When I click on the element "[hx-get^='/fragments/ai-agents/edit/'] >> nth=0"
22
+ Then "#editor-modal.uk-open" should be visible
23
+ And "#editor-modal .ace_editor" should be visible
@@ -0,0 +1,63 @@
1
+ Feature: Talking to the assistant
2
+ The panel takes dictation and reads its answer back. Both are the browser's own — no key, no
3
+ service, nothing leaves the machine — so what is asserted here is that the component was actually
4
+ handed a working configuration, not that a remote service replied.
5
+
6
+ # Asserting the button EXISTS, not that speech works: a scenario cannot speak, and a browser will
7
+ # not grant a microphone to an automated run. What can go wrong without anyone noticing is the
8
+ # button being absent, or present with a config deep-chat silently ignored — so that is the target.
9
+ Scenario: The assistant offers a microphone
10
+ Given I am on "/"
11
+ Then "deep-chat" should be attached
12
+ And the assistant should offer voice input
13
+ And the assistant should read its replies aloud
14
+
15
+ # The microphone is what says voice is available. It used to be said in the placeholder as well
16
+ # ("Ask about dev… (or use voice)"), but the composer ships with no placeholder at all now: an
17
+ # empty field with a cursor in it already says "type here", and a prompt that suggests what to ask
18
+ # narrows what gets asked. So the button has to be there AND have a name — an icon-only control
19
+ # with no accessible name is a button a screen reader announces as nothing.
20
+ Scenario: The microphone is there, and it is named
21
+ Given I am on "/"
22
+ Then the assistant should show a named microphone button
23
+
24
+ # Nothing drawn in the box, and the box still has a name. These are one scenario because they are
25
+ # in tension: deep-chat resolves the placeholder as `text || "Ask me anything!"`, so an empty
26
+ # string does not remove its placeholder, it restores deep-chat's — and the text it is given also
27
+ # becomes the composer's aria-label. Removing what is drawn without removing the name is the whole
28
+ # trick, and either half alone is a bug.
29
+ Scenario: The composer shows no placeholder, and is still named
30
+ Given I am on "/"
31
+ Then the assistant composer should draw no placeholder
32
+ And the assistant composer should have an accessible name
33
+
34
+ # The recogniser's language is the setting that decides whether dictation works in a second
35
+ # language at all: left on the wrong one it does not fail, it returns confident nonsense. So a
36
+ # language is always pinned rather than inherited — en-US ships, and the dropdown offers the rest.
37
+ Scenario: A dictation language is always pinned, never left to chance
38
+ Given I am on "/"
39
+ Then the assistant speech language should be pinned
40
+
41
+ # An empty phrase is not a disabled command. If `commands` were built from blank strings, a
42
+ # command could fire on almost anything dictated — so with none configured the key must be absent.
43
+ Scenario: No voice commands are armed unless one is configured
44
+ Given I am on "/"
45
+ Then the assistant should have no voice commands
46
+
47
+ Scenario: Voice styling follows the theme rather than deep-chat's stock colours
48
+ Given I am on "/"
49
+ Then the assistant speech colours should be design tokens
50
+
51
+ # The panel is on every workspace page, not only the home page, and its context travels with it.
52
+ Scenario: A workspace page gets the same microphone
53
+ Given I am on "/dev"
54
+ Then "deep-chat" should be attached
55
+ And the assistant should offer voice input
56
+
57
+ # A browser with no synthesis voices accepts speak(), fires error: synthesis-failed, and is silent.
58
+ # Nothing reported it, so "read replies aloud" looked switched on and did nothing at all — the worst
59
+ # state a setting can be in.
60
+ Scenario: The settings page says which voices this browser actually has
61
+ Given I am on "/settings"
62
+ Then "[data-voice-name]" should be attached
63
+ And the voice list should report what this browser has
@@ -0,0 +1,39 @@
1
+ Feature: The checks a project already declares
2
+ Every package here carries its own quality scripts. The dashboard offers them without inventing any.
3
+
4
+ # One quality menu, not two. On a packages workspace there is no browser suite at all, and on a
5
+ # site there was no checks control — a separate button would have been the only one present in
6
+ # each case. They live together under Tests, with the checks nested inside it.
7
+ Scenario: A package row offers its checks inside the Tests menu
8
+ Given I am on "/products"
9
+ Then the element ".project-row .act-menu-tests" should be displayed
10
+ And ".act-menu-tests .act-menu-checks" should be attached
11
+ And I should see "Tests"
12
+
13
+ Scenario: The button says what the package covers without running anything
14
+ Given I am on "/products"
15
+ Then the element ".act-menu-tests + .act-status" should be displayed
16
+ And "[hx-post='/actions/checks-plan']" should be attached
17
+
18
+ # The pair is the point: the canonical name is what you type (--only js), the code beside it is
19
+ # the script the package itself declared. Listing them together is how the mapping is checkable.
20
+ Scenario: The nested menu lists each alias against the script it runs
21
+ Given I am on "/products"
22
+ Then ".checks-sub" should be attached
23
+ And ".checks-sub .checks-alias" should be attached
24
+ And ".checks-sub .checks-script" should be attached
25
+
26
+ @needs-check-aliases
27
+ Scenario: Each alias can be run on its own, and so can all of them
28
+ Given I am on "/products"
29
+ Then "[hx-post='/actions/checks']" should be attached
30
+ And "[hx-vals*='only']" should be attached
31
+
32
+ # A site gets the same nested list: its package.json declares the same vocabulary, and its
33
+ # ddev-dev-tools add-on adds the PHP and Twig checks on top.
34
+ Scenario: A site keeps its testing verbs and gains the checks beside them
35
+ Given I am on "/dev"
36
+ Then the element ".project-row .act-menu-tests" should be displayed
37
+ And "[hx-post='/actions/testing-configure']" should be attached
38
+ And "[hx-post='/actions/testing-run']" should be attached
39
+ And ".act-menu-tests .act-menu-checks" should be attached
@@ -0,0 +1,30 @@
1
+ Feature: Cloning a project
2
+ The clone modal validates before anything runs, and never clones over an
3
+ existing project or a name DDEV already tracks.
4
+
5
+ Scenario: The DDEV menu offers the clone
6
+ Given I am on "/dev"
7
+ Then "[hx-get^='/fragments/projects/clone-form/dev/']" should be attached
8
+
9
+ Scenario: The form arrives prefilled, with the live check wired
10
+ Given I am on "/fragments/projects/clone-form/dev/v11x00b1i18n"
11
+ Then "input[name='to'][value='v11x00b1i18n-copy']" should be attached
12
+ And "select[name='toWorkspace']" should be attached
13
+ And "[hx-get='/fragments/projects/clone-check']" should be attached
14
+ And "textarea[name='prompt']" should be attached
15
+
16
+ Scenario: The live check refuses an existing project
17
+ Given I am on "/fragments/projects/clone-check?to=v11x00b1i18n&toWorkspace=dev"
18
+ Then I should see "already exists"
19
+
20
+ Scenario: The live check blesses a free name
21
+ Given I am on "/fragments/projects/clone-check?to=surely-free-name-zz&toWorkspace=dev"
22
+ Then I should see "is free"
23
+
24
+ Scenario: Cloning over an existing project is refused server-side too
25
+ When I post "/actions/clone-project" with '{"workspace":"dev","projectName":"v11x00b1i18n","to":"v11x00b1i18n","toWorkspace":"dev"}'
26
+ Then the action response should contain "already exists"
27
+
28
+ Scenario: A source that does not exist is refused
29
+ When I post "/actions/clone-project" with '{"workspace":"dev","projectName":"no-such-src","to":"whatever-zz","toWorkspace":"dev"}'
30
+ Then the action response should contain "Project not found"