@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,205 @@
1
+ /**
2
+ * The checks resolver: what a package declares, and which of it is safe to run.
3
+ *
4
+ * This is the one place the alias map lives — the shell verb shells out to it and the dashboard
5
+ * requires it — so it is the one place worth testing hard. Every case here is a real shape found in
6
+ * this workspace, named in the comment, because a rule invented for a package that does not exist is
7
+ * a rule nobody can check.
8
+ *
9
+ * Fixtures are written to a temp directory: the resolver reads package.json, the lockfiles and
10
+ * node_modules from disk, and stubbing fs would test the stub.
11
+ */
12
+
13
+ const { test } = require('node:test');
14
+ const assert = require('node:assert');
15
+ const fs = require('fs');
16
+ const os = require('os');
17
+ const path = require('path');
18
+
19
+ // Reached through WORKSPACE_ROOT rather than by counting ../ : the dashboard is bind-mounted at
20
+ // /var/www/html inside its container, where a relative path out of the app leaves the mount
21
+ // altogether. The app itself resolves the resolver the same way, in checks.js.
22
+ const { ROOT } = require('../../workspaces');
23
+ const {
24
+ resolvePlan, resolveRunner, resolveDeps, MASK, DENY_COMMAND, E2E_OK,
25
+ } = require(path.join(ROOT, 'core', 'scripts', 'libs', 'checks_plan'));
26
+
27
+ // A package on disk. `installed` fakes node_modules/.bin, which is what "runnable" means.
28
+ function pkg(scripts, { installed = true, lock = 'yarn.lock', packageManager = null, extra = {} } = {}) {
29
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ws-checks-'));
30
+ const json = { name: 'fixture', scripts };
31
+ if (packageManager) json.packageManager = packageManager;
32
+ fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify(json));
33
+ if (lock) fs.writeFileSync(path.join(dir, lock), '');
34
+ if (installed) fs.mkdirSync(path.join(dir, 'node_modules', '.bin'), { recursive: true });
35
+ for (const [rel, body] of Object.entries(extra)) {
36
+ fs.mkdirSync(path.join(dir, path.dirname(rel)), { recursive: true });
37
+ fs.writeFileSync(path.join(dir, rel), body);
38
+ }
39
+ return dir;
40
+ }
41
+
42
+ const row = (plan, id) => plan.rows.find((r) => r.id === id);
43
+
44
+ test('a package is read in its own vocabulary, whatever it calls things', () => {
45
+ // themes/vartheme_bs5 and 21 products carry exactly this set.
46
+ const plan = resolvePlan(pkg({
47
+ 'lint:js': 'eslint .', 'lint:css': 'stylelint "**/*.css"', 'lint:yaml': 'eslint --ext .yml .',
48
+ spellcheck: 'cspell lint .',
49
+ }));
50
+ assert.equal(row(plan, 'js').status, 'ready');
51
+ assert.equal(row(plan, 'js').script, 'lint:js');
52
+ assert.equal(row(plan, 'css').status, 'ready');
53
+ assert.equal(row(plan, 'yaml').status, 'ready');
54
+ assert.equal(row(plan, 'spelling').script, 'spellcheck');
55
+ // And says plainly what it does not cover.
56
+ assert.equal(row(plan, 'types').status, 'none');
57
+ assert.equal(row(plan, 'types').reason, 'not declared');
58
+ });
59
+
60
+ test('a package that spells them differently resolves to the same checks', () => {
61
+ // modules/canvas: lint:eslint / lint:stylelint / lint:cspell / lint:tsc / lint:prettier.
62
+ const plan = resolvePlan(pkg({
63
+ 'lint:eslint': 'eslint src', 'lint:stylelint': 'stylelint "src/**/*.css"',
64
+ 'lint:cspell': 'cspell .', 'lint:tsc': 'tsc --noEmit', 'lint:prettier': 'prettier --check .',
65
+ }));
66
+ for (const id of ['js', 'css', 'spelling', 'types', 'format']) {
67
+ assert.equal(row(plan, id).status, 'ready', `${id} should resolve`);
68
+ }
69
+ });
70
+
71
+ test('an aggregate does not run beside the parts it fans out to', () => {
72
+ // modules/canvas declares `lint` (concurrently) AND every part. Running both is double the work.
73
+ const plan = resolvePlan(pkg({ lint: 'concurrently "yarn lint:js" "yarn lint:css"', 'lint:js': 'eslint .', 'lint:css': 'stylelint "*.css"' }));
74
+ assert.ok(!plan.rows.some((r) => r.id === 'lint'), 'the aggregate row is dropped');
75
+ assert.equal(row(plan, 'js').status, 'ready');
76
+ });
77
+
78
+ test('an aggregate IS used when a package declares nothing else', () => {
79
+ const plan = resolvePlan(pkg({ lint: 'eslint . && stylelint "*.css"' }));
80
+ const agg = plan.rows.find((r) => r.id === 'lint');
81
+ assert.ok(agg, 'the aggregate row is emitted');
82
+ assert.equal(agg.status, 'ready');
83
+ });
84
+
85
+ test('a masked script fails the run rather than passing quietly', () => {
86
+ // modules/commerce_cart_flyout masks three of its lint scripts exactly like this.
87
+ const plan = resolvePlan(pkg({ 'lint:css': 'stylelint "**/*.css" || exit 0' }));
88
+ assert.equal(row(plan, 'css').status, 'masked');
89
+ assert.match(row(plan, 'css').reason, /would be a lie/);
90
+ assert.ok(MASK.test('foo || true'), 'and || true is the same lie');
91
+ });
92
+
93
+ test('a script that writes is refused, however it is named', () => {
94
+ // modules/gin_lb calls its writer `lint-all`; themes/vartheme_bs5 ends theme:build with --fix.
95
+ const byName = resolvePlan(pkg({ 'lint:js': 'eslint "src/**/*.js" --fix' }));
96
+ assert.equal(row(byName, 'js').status, 'refused');
97
+ assert.match(row(byName, 'js').reason, /--fix/);
98
+ assert.ok(DENY_COMMAND.test('prettier --write .'));
99
+ assert.ok(DENY_COMMAND.test('nodemon server.js'));
100
+ assert.ok(DENY_COMMAND.test('lint-staged'));
101
+ });
102
+
103
+ test('a stylelint call that names no files is refused, because it checks nothing', () => {
104
+ // 21 of the 45 lint:css declarations in this tree are this shape.
105
+ const plan = resolvePlan(pkg({ 'lint:css': 'stylelint --config=.stylelintrc.json' }));
106
+ assert.equal(row(plan, 'css').status, 'refused');
107
+ assert.match(row(plan, 'css').reason, /names no files/);
108
+ // With a glob it is fine.
109
+ assert.equal(row(resolvePlan(pkg({ 'lint:css': 'stylelint --config=.stylelintrc.json "**/*.css"' })), 'css').status, 'ready');
110
+ });
111
+
112
+ test('the browser suite is off unless asked for, and never on a package', () => {
113
+ const scripts = { 'test:chromium': 'BROWSER=chromium cucumber-js --config cucumber.js' };
114
+ const off = resolvePlan(pkg(scripts));
115
+ assert.equal(off.rows.find((r) => r.id === 'e2e').status, 'none');
116
+ assert.match(off.rows.find((r) => r.id === 'e2e').reason, /add --e2e/);
117
+
118
+ const onPackage = resolvePlan(pkg(scripts), { e2e: true });
119
+ assert.equal(row(onPackage, 'e2e').status, 'refused');
120
+ assert.match(row(onPackage, 'e2e').reason, /no site to test against/);
121
+
122
+ const onSite = resolvePlan(pkg(scripts, { extra: { '.ddev/config.yaml': 'name: x\n' } }), { e2e: true });
123
+ assert.equal(row(onSite, 'e2e').status, 'ready');
124
+ });
125
+
126
+ test('a test script that is not a browser suite is not treated as one', () => {
127
+ // products/bootstrap_styles: `test` is a build. Running it as a suite would be wrong twice over.
128
+ const plan = resolvePlan(pkg({ test: 'npm run build && node scripts/check.js' }), { e2e: true });
129
+ assert.equal(row(plan, 'e2e').status, 'none');
130
+ assert.ok(E2E_OK.test('cucumber-js --config cucumber.js'));
131
+ assert.ok(E2E_OK.test('playwright test'));
132
+ assert.ok(!E2E_OK.test('jest'));
133
+ });
134
+
135
+ test('phpcs is skipped, never reported green, when the binary is not there', () => {
136
+ // 76 packages declare phpcs; the binary is a composer one and lives in a site, not in node_modules.
137
+ const without = resolvePlan(pkg({ phpcs: 'phpcs --standard=./.phpcs.xml .' }));
138
+ assert.equal(row(without, 'php').status, 'skipped');
139
+ assert.match(row(without, 'php').reason, /no vendor\/bin\/phpcs/);
140
+
141
+ const with_ = resolvePlan(pkg({ phpcs: 'phpcs .' }, { extra: { 'vendor/bin/phpcs': '#!/bin/sh\n' } }));
142
+ assert.equal(row(with_, 'php').status, 'ready');
143
+ });
144
+
145
+ test('a site carrying the ddev-dev-tools add-on gets php and twig from it', () => {
146
+ const dir = pkg({}, { extra: {
147
+ '.ddev/config.yaml': 'name: x\n',
148
+ '.ddev/commands/web/dev-phpcs': '#!/bin/bash\n',
149
+ } });
150
+ const plan = resolvePlan(dir);
151
+ assert.equal(row(plan, 'php').status, 'ready');
152
+ assert.equal(row(plan, 'php').command, 'ddev dev-phpcs --all');
153
+ assert.equal(row(plan, 'twig').command, 'ddev dev-twigcs --all');
154
+ });
155
+
156
+ test('the package manager is chosen from what the package says, and says why', () => {
157
+ assert.deepEqual(resolveRunner(pkg({}, { lock: 'yarn.lock' })), { runner: 'yarn', reason: 'yarn.lock' });
158
+ assert.deepEqual(resolveRunner(pkg({}, { lock: 'package-lock.json' })), { runner: 'npm', reason: 'package-lock.json' });
159
+ const berry = resolveRunner(pkg({}, { lock: null, packageManager: 'yarn@4.9.3' }));
160
+ assert.equal(berry.runner, 'yarn');
161
+ assert.match(berry.reason, /yarn@4\.9\.3/);
162
+ const none = resolveRunner(pkg({}, { lock: null }));
163
+ assert.equal(none.runner, null);
164
+ assert.match(none.reason, /pass --runner/);
165
+ });
166
+
167
+ test('a stale .pnp.cjs is not an installed package', () => {
168
+ // products/webship_core: a PnP file left over beside a .yarnrc.yml that now asks for node-modules.
169
+ // Believing it made every check fail with "Couldn't find the node_modules state file".
170
+ const stale = pkg({ 'lint:yaml': 'eslint --ext .yml .' }, {
171
+ installed: false, extra: { '.pnp.cjs': '// old', '.yarnrc.yml': 'nodeLinker: node-modules\n' },
172
+ });
173
+ assert.equal(resolveDeps(stale).installed, false);
174
+ assert.match(resolveDeps(stale).evidence, /stale/);
175
+ assert.equal(row(resolvePlan(stale), 'yaml').status, 'deps-missing');
176
+
177
+ // Real PnP, with no nodeLinker override, is runnable and has no node_modules at all.
178
+ const pnp = pkg({}, { installed: false, extra: { '.pnp.cjs': '// real' } });
179
+ assert.equal(resolveDeps(pnp).installed, true);
180
+ });
181
+
182
+ test('--only and --skip narrow the run without hiding what exists', () => {
183
+ const scripts = { 'lint:js': 'eslint .', 'lint:css': 'stylelint "*.css"', 'lint:yaml': 'eslint --ext .yml .' };
184
+ const only = resolvePlan(pkg(scripts), { only: 'js' });
185
+ assert.equal(row(only, 'js').status, 'ready');
186
+ assert.equal(row(only, 'css').status, 'none');
187
+ assert.match(row(only, 'css').reason, /not in --only/);
188
+
189
+ const skip = resolvePlan(pkg(scripts), { skip: 'css' });
190
+ assert.equal(row(skip, 'js').status, 'ready');
191
+ assert.equal(row(skip, 'css').status, 'skipped');
192
+ });
193
+
194
+ test('every row is always reported, so a gap is visible rather than absent', () => {
195
+ const plan = resolvePlan(pkg({ 'lint:js': 'eslint .' }));
196
+ assert.deepEqual(plan.rows.map((r) => r.id),
197
+ ['format', 'js', 'css', 'yaml', 'spelling', 'types', 'php', 'twig', 'e2e']);
198
+ });
199
+
200
+ test('a directory with no package.json is not an error, it is a package with nothing to run', () => {
201
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ws-checks-'));
202
+ const plan = resolvePlan(dir);
203
+ assert.equal(plan.hasPackageJson, false);
204
+ assert.ok(plan.rows.every((r) => r.status === 'none'));
205
+ });
@@ -0,0 +1,182 @@
1
+ /**
2
+ * The Claude Code arguments the New screen form offers, read from `claude --help`.
3
+ *
4
+ * The form used to carry a permission-mode select and one free-text box called "more claude options",
5
+ * which meant knowing the flags by heart and typing them with no check at all until the screen either
6
+ * started or died. The CLI documents every one of them, so the list is PARSED rather than kept here by
7
+ * hand — the same reason the icon chooser parses the vendored sprite and builder-args.js reads a
8
+ * builder's own argparse block.
9
+ *
10
+ * Parsed against a fixture of the real help text, so these tests do not need claude on the machine and
11
+ * do not change meaning when it is updated.
12
+ */
13
+
14
+ const assert = require('node:assert');
15
+ const { test } = require('node:test');
16
+
17
+ const ca = require('../../claude-args');
18
+
19
+ // The two-column shape `claude --help` prints: flags, a wide gap, then a description that wraps — and
20
+ // that starts on the next line when the flags are too long for the column.
21
+ const HELP = `Usage: claude [options] [command] [prompt]
22
+
23
+ Options:
24
+ --add-dir <directories...> Additional directories to allow tool
25
+ access to
26
+ --agent <agent> Agent for the current session. Overrides
27
+ the 'agent' setting.
28
+ --allowedTools, --allowed-tools <tools...>
29
+ Comma or space-separated list of tool names to allow (e.g. "Bash(git *)
30
+ Edit")
31
+ --autocompact <auto|tokens> Auto-compact window size (auto, or
32
+ 100k-1M tokens)
33
+ --bg, --background Start the session in the background and
34
+ return immediately
35
+ -c, --continue Continue the most recent conversation in
36
+ the current directory
37
+ --debug [filter] Enable debug mode with optional category
38
+ filtering
39
+ --max-budget-usd <amount> Maximum dollar amount to spend on API
40
+ calls (only works with --print)
41
+ --model <model> Model for the current session
42
+ --output-format <format> Output format (only works with --print):
43
+ "text" (default) (choices: "text",
44
+ "json")
45
+ --permission-mode <mode> Permission mode to use for the session
46
+ (choices: "acceptEdits", "auto",
47
+ "bypassPermissions", "plan")
48
+ -p, --print Print response and exit
49
+ --prompt-suggestions [value] Enable prompt suggestions (choices:
50
+ "true", "false", preset: "true")
51
+ -r, --resume [value] Resume a conversation by session ID
52
+ --verbose Override verbose mode setting from config
53
+ -v, --version Output the version number
54
+
55
+ Commands:
56
+ doctor Check the health of your installation
57
+ `;
58
+
59
+ const parsed = ca.parseOptions(HELP);
60
+ const by = (name) => parsed.find((o) => o.name === name);
61
+
62
+ test('the two columns are told apart, wrapped description and all', () => {
63
+ assert.strictEqual(by('--model').description, 'Model for the current session');
64
+ assert.strictEqual(by('--add-dir').description, 'Additional directories to allow tool access to',
65
+ 'a description that wraps is one description, not two options');
66
+ assert.strictEqual(by('--allowed-tools').description,
67
+ 'Comma or space-separated list of tool names to allow (e.g. "Bash(git *) Edit")',
68
+ 'and one that starts on the line below still belongs to the flags above it');
69
+ });
70
+
71
+ test('a flag says what it takes, and whether it repeats', () => {
72
+ assert.strictEqual(by('--verbose').takes, 'none');
73
+ assert.strictEqual(by('--model').takes, 'required');
74
+ assert.strictEqual(by('--debug').takes, 'optional');
75
+ assert.strictEqual(by('--add-dir').repeatable, true, '<directories...> is a list');
76
+ assert.strictEqual(by('--model').repeatable, false);
77
+ assert.strictEqual(by('--add-dir').placeholder, 'directories');
78
+ });
79
+
80
+ test('aliases are kept, and the long one is the name', () => {
81
+ assert.deepStrictEqual(by('--allowed-tools').flags, ['--allowedTools', '--allowed-tools']);
82
+ assert.deepStrictEqual(by('--continue').flags, ['-c', '--continue']);
83
+ });
84
+
85
+ test('only what the CLI calls a choice is a choice', () => {
86
+ assert.deepStrictEqual(by('--permission-mode').choices, ['acceptEdits', 'auto', 'bypassPermissions', 'plan']);
87
+ assert.deepStrictEqual(by('--prompt-suggestions').choices, ['true', 'false'],
88
+ 'the preset: inside the same parenthesis is not one of them');
89
+ assert.deepStrictEqual(by('--autocompact').choices, [],
90
+ '<auto|tokens> is the SHAPE of the value ("auto, or 100k-1M tokens") — a select there would refuse 200k');
91
+ assert.strictEqual(by('--autocompact').placeholder, 'auto|tokens', 'it is a placeholder instead');
92
+ });
93
+
94
+ test('what only works with --print is not offered: a screen IS the interactive session', () => {
95
+ const offered = parsed.filter(ca.usableOnAScreen).map((o) => o.name);
96
+ assert.ok(!offered.includes('--max-budget-usd'));
97
+ assert.ok(!offered.includes('--output-format'));
98
+ assert.ok(!offered.includes('--print'));
99
+ });
100
+
101
+ test('what starts the session somewhere else is not offered either', () => {
102
+ const offered = parsed.filter(ca.usableOnAScreen).map((o) => o.name);
103
+ assert.ok(!offered.includes('--background'), 'it returns immediately, so the screen would hold nothing');
104
+ assert.ok(!offered.includes('--version'));
105
+ for (const name of Object.keys(ca.WITHHELD)) {
106
+ assert.ok(ca.WITHHELD[name].length > 10, `${name} says WHY it is held back, so the decision can be argued with`);
107
+ }
108
+ });
109
+
110
+ test('what the form already asks about is not asked twice', () => {
111
+ const offered = parsed.filter(ca.usableOnAScreen).map((o) => o.name);
112
+ assert.ok(!offered.includes('--resume'), 'the Resume row asks this, and checks the id against that directory');
113
+ assert.ok(!offered.includes('--continue'));
114
+ });
115
+
116
+ test('everything else IS offered, including a flag added after this was written', () => {
117
+ const offered = parsed.filter(ca.usableOnAScreen).map((o) => o.name);
118
+ assert.deepStrictEqual(offered.sort(), ['--add-dir', '--agent', '--allowed-tools', '--autocompact',
119
+ '--debug', '--model', '--permission-mode', '--prompt-suggestions', '--verbose'].sort());
120
+ });
121
+
122
+ test('a field name cannot collide with the form own fields', () => {
123
+ assert.strictEqual(ca.fieldName('--add-dir'), 'arg_add_dir');
124
+ assert.strictEqual(ca.fieldName('--permission-mode'), 'arg_permission_mode');
125
+ for (const own of ['session', 'target', 'workspace', 'kind', 'command', 'resume', 'resume_on']) {
126
+ assert.notStrictEqual(ca.fieldName(`--${own}`), own);
127
+ }
128
+ });
129
+
130
+ test('a submitted form becomes shell words, in the order the CLI printed them', () => {
131
+ const { words, problems } = ca.flagsFromForm(parsed.filter(ca.usableOnAScreen), {
132
+ arg_model: 'opus', arg_permission_mode: 'bypassPermissions', arg_verbose: '1',
133
+ });
134
+ assert.deepStrictEqual(problems, []);
135
+ assert.deepStrictEqual(words, ['--model', 'opus', '--permission-mode', 'bypassPermissions', '--verbose']);
136
+ });
137
+
138
+ test('a repeatable flag takes its words one at a time, not as one argument', () => {
139
+ const { words } = ca.flagsFromForm(parsed.filter(ca.usableOnAScreen), { arg_add_dir: '../shared ../other' });
140
+ assert.deepStrictEqual(words, ['--add-dir', '../shared', '../other']);
141
+ });
142
+
143
+ test('an empty field asks for nothing', () => {
144
+ const { words } = ca.flagsFromForm(parsed.filter(ca.usableOnAScreen), { arg_model: '', arg_agent: ' ' });
145
+ assert.deepStrictEqual(words, []);
146
+ });
147
+
148
+ test('a value off the choice list is refused BY NAME, not dropped', () => {
149
+ const { words, problems } = ca.flagsFromForm(parsed.filter(ca.usableOnAScreen), { arg_permission_mode: 'whatever' });
150
+ assert.deepStrictEqual(words, []);
151
+ assert.match(problems[0], /--permission-mode does not take/);
152
+ });
153
+
154
+ test('nothing a shell would read reaches the command line', () => {
155
+ for (const bad of ['opus; rm -rf /', 'opus && curl x', '$(whoami)', '`id`', 'a\nb', "it's", 'x|y', 'a>b']) {
156
+ const { words, problems } = ca.flagsFromForm(parsed.filter(ca.usableOnAScreen), { arg_model: bad });
157
+ assert.deepStrictEqual(words, [], `refused: ${JSON.stringify(bad)}`);
158
+ assert.strictEqual(problems.length, 1, `and said so: ${JSON.stringify(bad)}`);
159
+ }
160
+ // The shapes a real value takes are all allowed.
161
+ for (const good of ['opus', 'claude-fable-5', '../shared', 'Bash,Edit,Read', 'user,project', 'a@b.example']) {
162
+ const { problems } = ca.flagsFromForm(parsed.filter(ca.usableOnAScreen), { arg_model: good });
163
+ assert.deepStrictEqual(problems, [], `allowed: ${good}`);
164
+ }
165
+ });
166
+
167
+ test('a field naming no option of this CLI is ignored, not passed through', () => {
168
+ const { words } = ca.flagsFromForm(parsed.filter(ca.usableOnAScreen), { arg_made_up: 'x', options: '--whatever' });
169
+ assert.deepStrictEqual(words, []);
170
+ });
171
+
172
+ test('the four asked about every time are asked about first', () => {
173
+ assert.deepStrictEqual(ca.COMMON, ['--model', '--permission-mode', '--effort', '--agent']);
174
+ });
175
+
176
+ test('help that says nothing is an error, not an empty list of options', async () => {
177
+ ca.forget();
178
+ const { options, error } = await ca.claudeOptions();
179
+ // On a machine with claude installed this reads the real help; on one without, it is the error path.
180
+ // Either is correct — what must never happen is an empty list reported as success.
181
+ assert.ok(error ? options.length === 0 : options.length > 0);
182
+ });
@@ -0,0 +1,15 @@
1
+ const test = require('node:test');
2
+ const assert = require('node:assert/strict');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const server = fs.readFileSync(path.join(__dirname, '..', '..', 'server.js'), 'utf8');
7
+
8
+ test('the SCREENS menu offers Claude Code in the same folder, beside New screen here', () => {
9
+ assert.match(server, /screen-new-form\?workspace=\$\{encodeURIComponent\(sm\[1\]\)\}&amp;project=\$\{encodeURIComponent\(sm\[2\]\)\}&amp;kind=claude/);
10
+ assert.match(server, /Claude Code here…/);
11
+ });
12
+
13
+ test('the New screen form opens on Claude Code when asked, keeping the place it was opened for', () => {
14
+ assert.match(server, /if \(q\.get\('kind'\) === 'claude'\) pre = \{ \.\.\.\(pre \|\| \{\}\), kind: 'claude' \};/);
15
+ });
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Resuming a Claude Code session in a screen.
3
+ *
4
+ * A screen that dies — the machine reboots, the terminal closes, `screen -wipe` takes a dead one — does
5
+ * not take the conversation with it: the CLI keeps every session's transcript under
6
+ * ~/.claude/projects/<the directory, slashes turned into dashes>/<session id>.jsonl, and
7
+ * `claude --resume <id>` from that directory picks it back up.
8
+ *
9
+ * The thing that cost a screen to find: what is on disk is TWO things, and only one can be resumed.
10
+ * `<id>.jsonl` is the transcript, and `<id>/` is that session's tool results and subagents — which
11
+ * exists on its own for a session whose transcript was never written. Offering one of those gave a
12
+ * screen that answered "No conversation found with session ID" and died in the same second, which from
13
+ * the dashboard looks exactly like the feature being broken.
14
+ */
15
+
16
+ const assert = require('node:assert');
17
+ const { test } = require('node:test');
18
+ const fs = require('fs');
19
+ const os = require('os');
20
+ const path = require('path');
21
+
22
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-home-'));
23
+ const realHome = os.homedir;
24
+ os.homedir = () => home;
25
+ process.on('exit', () => { os.homedir = realHome; fs.rmSync(home, { recursive: true, force: true }); });
26
+
27
+ const cs = require('../../claude-sessions');
28
+
29
+ const DIR = '/home/someone/workspace/dev/ha1x00b1';
30
+ const SLUG = '-home-someone-workspace-dev-ha1x00b1';
31
+ const projectDir = path.join(home, '.claude', 'projects', SLUG);
32
+ fs.mkdirSync(projectDir, { recursive: true });
33
+
34
+ const id = (n) => `${'0'.repeat(8 - String(n).length)}${n}-10da-41df-b92f-51c00661b38c`;
35
+ const transcript = (n, lines) => fs.writeFileSync(path.join(projectDir, `${id(n)}.jsonl`), lines);
36
+ const userLine = (text) => `${JSON.stringify({ type: 'user', message: { role: 'user', content: text } })}\n`;
37
+
38
+ test("the CLI's own name for a directory is the absolute path with every slash turned into a dash", () => {
39
+ assert.strictEqual(cs.projectSlug(DIR), SLUG);
40
+ assert.strictEqual(cs.projectSlug('/home/someone/workspace/dev/ha1x00b1/'), SLUG, 'a trailing slash is the same directory');
41
+ assert.strictEqual(cs.projectDirFor(DIR), projectDir);
42
+ });
43
+
44
+ test('a session id is the uuid --resume takes, and nothing else is handed to a shell', () => {
45
+ assert.ok(cs.isSessionId('cc9ce3fd-10da-41df-b92f-51c00661b38c'));
46
+ assert.ok(cs.isSessionId('CC9CE3FD-10DA-41DF-B92F-51C00661B38C'), 'the CLI writes them lowercase but reads either');
47
+ assert.ok(!cs.isSessionId('cc9ce3fd'));
48
+ assert.ok(!cs.isSessionId('$(rm -rf ~)'));
49
+ assert.ok(!cs.isSessionId('../../../etc/passwd'));
50
+ assert.ok(!cs.isSessionId(''));
51
+ });
52
+
53
+ test('a directory with no history at all lists nothing, rather than failing', () => {
54
+ assert.deepStrictEqual(cs.sessionsFor('/home/someone/workspace/dev/never-used'), []);
55
+ assert.strictEqual(cs.hasSessions('/home/someone/workspace/dev/never-used'), false);
56
+ });
57
+
58
+ test('a transcript is resumable; a directory with no transcript is listed but never offered', () => {
59
+ transcript(1, userLine('build me a Webship 11 site'));
60
+ fs.mkdirSync(path.join(projectDir, id(2)), { recursive: true }); // tool results only: --resume finds nothing
61
+ fs.writeFileSync(path.join(projectDir, `${id(3)}.jsonl`), ''); // started and never got going
62
+
63
+ const all = cs.sessionsFor(DIR);
64
+ assert.deepStrictEqual(all.map((x) => x.id).sort(), [id(1), id(2), id(3)].sort(), 'all three are on disk');
65
+ assert.deepStrictEqual(all.filter((x) => x.resumable).map((x) => x.id), [id(1)]);
66
+
67
+ const offered = cs.sessionsFor(DIR, { resumableOnly: true });
68
+ assert.deepStrictEqual(offered.map((x) => x.id), [id(1)],
69
+ 'an id --resume answers "No conversation found" for is a dead end, not a choice');
70
+ assert.strictEqual(cs.hasSessions(DIR), true);
71
+ });
72
+
73
+ test('the title is the first thing the person typed, so two sessions can be told apart', () => {
74
+ transcript(4, userLine('<system-reminder>a reminder</system-reminder> why does the install fail'));
75
+ const one = cs.sessionsFor(DIR).find((x) => x.id === id(4));
76
+ assert.strictEqual(one.title, 'a reminder why does the install fail',
77
+ 'the first thing typed, with the markup around it stripped rather than shown');
78
+
79
+ transcript(5, `${JSON.stringify({ type: 'user', message: { content: [{ type: 'text', text: 'a list of parts' }] } })}\n`);
80
+ assert.strictEqual(cs.sessionsFor(DIR).find((x) => x.id === id(5)).title, 'a list of parts');
81
+
82
+ transcript(6, 'half a line with no closing brace');
83
+ assert.strictEqual(cs.sessionsFor(DIR).find((x) => x.id === id(6)).title, '', 'an unreadable head is no title, not a crash');
84
+ });
85
+
86
+ test('newest first, and limited: the one just left is the one being looked for', () => {
87
+ const now = Date.now();
88
+ for (const n of [1, 4, 5, 6]) {
89
+ const f = path.join(projectDir, `${id(n)}.jsonl`);
90
+ fs.utimesSync(f, new Date(now - n * 60000), new Date(now - n * 60000));
91
+ }
92
+ const list = cs.sessionsFor(DIR, { resumableOnly: true });
93
+ assert.deepStrictEqual(list.map((x) => x.id), [id(1), id(4), id(5), id(6)]);
94
+ assert.strictEqual(cs.sessionsFor(DIR, { limit: 2 }).length, 2);
95
+ });
96
+
97
+ test('the memory directory is not a session', () => {
98
+ fs.mkdirSync(path.join(projectDir, 'memory'), { recursive: true });
99
+ assert.ok(!cs.sessionsFor(DIR).some((x) => x.id === 'memory'));
100
+ });
101
+
102
+ /* ------------------------------------------------------------------ *
103
+ * What the form and the runner do with it
104
+ * ------------------------------------------------------------------ */
105
+
106
+ const read = (f) => fs.readFileSync(path.join(__dirname, '..', '..', f), 'utf8');
107
+ const screens = read('screens.js');
108
+ const action = read('actions/screens.js');
109
+ const server = read('server.js');
110
+
111
+ test('only an id that is resumable IN THIS DIRECTORY reaches the command line', () => {
112
+ assert.match(action, /claudeSessions\.isSessionId\(wanted\)/, 'a uuid, so nothing else can be spliced in');
113
+ assert.match(action, /\.some\(\(x\) => x\.id === wanted\)/, "and one this directory actually has");
114
+ assert.match(action, /const wanted = form\.resume_on \? String\(form\.resume \|\| ''\)\.trim\(\) : '';/,
115
+ 'a select left on its default must not resume a session nobody asked to resume');
116
+ assert.match(action, /const resumeFlag = resumable \? `--resume \$\{resumable\}` : '';/);
117
+ });
118
+
119
+ test('the screen runs claude in the project directory, and nothing else changes', () => {
120
+ assert.match(action, /exec claude \$\{resumeFlag\} \$\{flags\}/);
121
+ assert.match(action, /No Claude session <code>/, 'an id that cannot be resumed is refused, not silently dropped');
122
+ });
123
+
124
+ test('the list offers only what --resume can open, and says so when there is nothing', () => {
125
+ assert.match(screens, /resumableOnly: true/);
126
+ assert.match(screens, /No Claude session to resume in/);
127
+ assert.match(screens, /no transcript written yet, so <code>--resume<\/code> cannot open/,
128
+ 'a session with no transcript is reported rather than quietly missing');
129
+ });
130
+
131
+ test('the select is disabled until the checkbox is ticked, and the newest is preselected', () => {
132
+ assert.match(screens, /type="checkbox" name="resume_on"/);
133
+ assert.match(screens, /<select class="uk-select uk-form-small" name="resume" disabled/);
134
+ assert.match(screens, /s\.disabled=!this\.checked;/);
135
+ assert.match(screens, /\$\{i === 0 \? ' selected' : ''\}/);
136
+ });
137
+
138
+ test('changing the project reloads the list, and that request does not close the modal', () => {
139
+ assert.match(screens, /hx-get="\/fragments\/claude-sessions"/);
140
+ // The place select asks for the list itself now, rather than the row listening for that select to
141
+ // change: the select is re-rendered whenever the workspace changes, and a listener on the row would
142
+ // have been listening for an element that is no longer the same one.
143
+ assert.match(screens, /hx-target="#claude-resume-row"/);
144
+ assert.match(screens, /hx-on::after:request="event\.stopPropagation\(\)"/,
145
+ "the form's own after:request hides the modal — this row's request must not reach it");
146
+ assert.match(server, /\/fragments\/claude-sessions/, 'and the route exists');
147
+ });
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The New screen form, on Claude Code: the model, the effort and the agent as lists, and the form
3
+ * opening where it was opened FROM.
4
+ *
5
+ * The bug underneath this one is the second half. A project row's SCREENS → "New screen here…" passes
6
+ * the workspace and the project, but the form only honoured them when a Jira key came with them — so
7
+ * it opened at the workspace ROOT, and a session started from it ran in ~/workspace instead of the
8
+ * project it was asked for. Nothing said so: the place selects simply read `root`.
9
+ */
10
+
11
+ const { test } = require('node:test');
12
+ const assert = require('node:assert');
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const claudeArgs = require('../../claude-args');
17
+ const server = fs.readFileSync(path.join(__dirname, '../../server.js'), 'utf8');
18
+ const screens = fs.readFileSync(path.join(__dirname, '../../screens.js'), 'utf8');
19
+
20
+ const HELP = `
21
+ Options:
22
+ --model <model> Model for the current session. Provide
23
+ an alias for the latest model (e.g.
24
+ 'fable', 'opus', or 'sonnet') or a
25
+ model's full name (e.g.
26
+ 'claude-fable-5').
27
+ --effort <level> Effort level for the current session
28
+ (low, medium, high, xhigh, max)
29
+ --autocompact <auto|tokens> When to compact (default: auto)
30
+ --agent <agent> Agent for the current session. Overrides
31
+ the 'agent' setting.
32
+ Commands:
33
+ `;
34
+
35
+ // No guard around this: a test that can skip itself silently is a test that can stop testing without
36
+ // anyone noticing.
37
+ const opt = (name) => claudeArgs.parseOptions(HELP).find((o) => o.name === name);
38
+
39
+ test('an option that enumerates its values in prose gets a closed list', () => {
40
+ // `--effort <level> … (low, medium, high, xhigh, max)` — a closed list said in a sentence rather
41
+ // than in the `(choices: …)` the help generator emits.
42
+ const effort = opt('--effort');
43
+ assert.deepEqual(effort.choices, ['low', 'medium', 'high', 'xhigh', 'max']);
44
+ });
45
+
46
+ test('a shape hint is still not a list of choices', () => {
47
+ // `<auto|tokens>` means "auto, or a number of tokens". Reading it as choices would refuse 200k, and
48
+ // the comment in claude-args.js says so; the prose rule must not undo that.
49
+ const auto = opt('--autocompact');
50
+ assert.deepEqual(auto.choices, [], 'pipes in the value placeholder are not choices');
51
+ // Nor is a lone default or a pair — three or more bare words is the bar.
52
+ assert.ok(!auto.choices.includes('auto'));
53
+ });
54
+
55
+ test('the model offers what the help NAMES, and still takes anything', () => {
56
+ const model = opt('--model');
57
+ assert.deepEqual(model.choices, [], 'never a closed list');
58
+ assert.ok(model.suggestions.includes('opus') && model.suggestions.includes('sonnet'),
59
+ 'the aliases the CLI itself gives as examples');
60
+ // A select here would refuse every model released after this help text was read.
61
+ assert.match(screens, /an OPEN list \(--model, --agent\) is an input with a datalist/);
62
+ });
63
+
64
+ test('the agents offered are the ones that exist, by their frontmatter name', () => {
65
+ const agents = claudeArgs.installedAgents();
66
+ assert.ok(Array.isArray(agents));
67
+ // A rules document in the same folder is not an agent: the CLI dispatches on a frontmatter `name`,
68
+ // and RULES.md has none. Blocklisting the docs by name would work until somebody added another.
69
+ assert.ok(!agents.includes('RULES'), 'prose in the agents folder is not an agent');
70
+ assert.match(require('fs').readFileSync(path.join(__dirname, '../../claude-args.js'), 'utf8'),
71
+ /if \(!\/\^---\\s\*\$\/m\.test/, 'frontmatter is what makes it an agent');
72
+ });
73
+
74
+ test('the four asked for every time are the four laid out first', () => {
75
+ assert.deepEqual(claudeArgs.COMMON, ['--model', '--permission-mode', '--effort', '--agent']);
76
+ });
77
+
78
+ test('the form opens on the project it was opened from', () => {
79
+ // Without this the whole feature is a session in the wrong directory, which is what it was.
80
+ assert.match(server, /if \(!pre\) \{[\s\S]{0,400}pre = \{ workspace: ws, target: `p:\$\{ws\}:\$\{proj\}` \}/);
81
+ assert.match(server, /pre = \{ workspace: ws, target: `ws:\$\{ws\}` \}/,
82
+ 'a workspace with no project still opens on that workspace');
83
+ assert.match(server, /opened at the workspace ROOT/, 'and the reason is written down');
84
+ });