@webship.co/workspace 1.0.0-beta1 → 1.0.0-rc1

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 (632) 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} +9 -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/{plugins-page.js → core/workspace-app/plugins-page.js} +28 -2
  169. package/{server.js → core/workspace-app/server.js} +18 -0
  170. package/core/workspace-app/tests/features/actions-rail.feature +44 -0
  171. package/core/workspace-app/tests/features/ai-items.feature +23 -0
  172. package/core/workspace-app/tests/features/assistant-speech.feature +63 -0
  173. package/core/workspace-app/tests/features/checks.feature +40 -0
  174. package/core/workspace-app/tests/features/clone-project.feature +30 -0
  175. package/core/workspace-app/tests/features/command-authoring.feature +58 -0
  176. package/core/workspace-app/tests/features/dashboard.feature +57 -0
  177. package/core/workspace-app/tests/features/menus.feature +29 -0
  178. package/core/workspace-app/tests/features/plugins/jira/jira.feature +47 -0
  179. package/core/workspace-app/tests/features/plugins.feature +20 -0
  180. package/core/workspace-app/tests/features/project-actions.feature +41 -0
  181. package/core/workspace-app/tests/features/screens.feature +29 -0
  182. package/core/workspace-app/tests/features/settings.feature +46 -0
  183. package/core/workspace-app/tests/features/test-reports.feature +32 -0
  184. package/core/workspace-app/tests/features/theme.feature +40 -0
  185. package/core/workspace-app/tests/step-definitions/actions-post.steps.js +25 -0
  186. package/core/workspace-app/tests/step-definitions/assistant-speech.steps.js +157 -0
  187. package/core/workspace-app/tests/step-definitions/clone-project.steps.js +47 -0
  188. package/core/workspace-app/tests/step-definitions/preconditions.steps.js +168 -0
  189. package/core/workspace-app/tests/unit/_isolated-config.js +31 -0
  190. package/core/workspace-app/tests/unit/accessibility.test.js +124 -0
  191. package/core/workspace-app/tests/unit/actions-rail-ux.test.js +56 -0
  192. package/core/workspace-app/tests/unit/actions-rail.test.js +51 -0
  193. package/core/workspace-app/tests/unit/ai-inventory.test.js +64 -0
  194. package/core/workspace-app/tests/unit/app-docs.test.js +64 -0
  195. package/core/workspace-app/tests/unit/assistant-avatar.test.js +110 -0
  196. package/core/workspace-app/tests/unit/assistant-cast.test.js +105 -0
  197. package/core/workspace-app/tests/unit/assistant-send-animation.test.js +15 -0
  198. package/core/workspace-app/tests/unit/assistant-suggestions.test.js +112 -0
  199. package/core/workspace-app/tests/unit/assistant-switches.test.js +53 -0
  200. package/core/workspace-app/tests/unit/assistant-views.test.js +146 -0
  201. package/core/workspace-app/tests/unit/assistant-working-indicator.test.js +49 -0
  202. package/core/workspace-app/tests/unit/assistant.test.js +61 -0
  203. package/core/workspace-app/tests/unit/audio-player.test.js +147 -0
  204. package/core/workspace-app/tests/unit/audio-voice.test.js +634 -0
  205. package/core/workspace-app/tests/unit/auth-google.test.js +180 -0
  206. package/core/workspace-app/tests/unit/autostart-watch.test.js +76 -0
  207. package/core/workspace-app/tests/unit/browsers.test.js +76 -0
  208. package/core/workspace-app/tests/unit/build-distribution.test.js +125 -0
  209. package/core/workspace-app/tests/unit/build-order.test.js +153 -0
  210. package/core/workspace-app/tests/unit/builder-args.test.js +113 -0
  211. package/core/workspace-app/tests/unit/builders.test.js +158 -0
  212. package/core/workspace-app/tests/unit/card-state-toggle.test.js +29 -0
  213. package/core/workspace-app/tests/unit/changelog.test.js +223 -0
  214. package/core/workspace-app/tests/unit/chat-thread.test.js +92 -0
  215. package/core/workspace-app/tests/unit/checks-plan.test.js +205 -0
  216. package/core/workspace-app/tests/unit/claude-args.test.js +182 -0
  217. package/core/workspace-app/tests/unit/claude-code-here.test.js +15 -0
  218. package/core/workspace-app/tests/unit/claude-resume.test.js +147 -0
  219. package/core/workspace-app/tests/unit/claude-screen-options.test.js +84 -0
  220. package/core/workspace-app/tests/unit/claude-session.test.js +41 -0
  221. package/core/workspace-app/tests/unit/command-flags.test.js +45 -0
  222. package/core/workspace-app/tests/unit/command-palette.test.js +105 -0
  223. package/core/workspace-app/tests/unit/commands-center.test.js +338 -0
  224. package/core/workspace-app/tests/unit/composer-menu.test.js +187 -0
  225. package/core/workspace-app/tests/unit/config-yaml.test.js +116 -0
  226. package/core/workspace-app/tests/unit/confirm-dialog.test.js +69 -0
  227. package/core/workspace-app/tests/unit/create-card.test.js +85 -0
  228. package/core/workspace-app/tests/unit/ddev-addons.test.js +144 -0
  229. package/core/workspace-app/tests/unit/ddev-auth-ssh.test.js +40 -0
  230. package/core/workspace-app/tests/unit/ddev-custom-commands.test.js +56 -0
  231. package/core/workspace-app/tests/unit/ddev-diagnose.test.js +57 -0
  232. package/core/workspace-app/tests/unit/ddev-domains.test.js +179 -0
  233. package/core/workspace-app/tests/unit/ddev-hostnames.test.js +143 -0
  234. package/core/workspace-app/tests/unit/ddev-import.test.js +82 -0
  235. package/core/workspace-app/tests/unit/ddev-mail.test.js +135 -0
  236. package/core/workspace-app/tests/unit/ddev-menu-and-create.test.js +28 -0
  237. package/core/workspace-app/tests/unit/ddev-mutagen.test.js +37 -0
  238. package/core/workspace-app/tests/unit/ddev-options.test.js +51 -0
  239. package/core/workspace-app/tests/unit/ddev-share.test.js +40 -0
  240. package/core/workspace-app/tests/unit/ddev-shim.test.js +57 -0
  241. package/core/workspace-app/tests/unit/ddev-snapshots.test.js +142 -0
  242. package/core/workspace-app/tests/unit/ddev-start-daemons.test.js +62 -0
  243. package/core/workspace-app/tests/unit/ddev-xdebug.test.js +41 -0
  244. package/core/workspace-app/tests/unit/deep-chat-origin.test.js +38 -0
  245. package/core/workspace-app/tests/unit/density.test.js +52 -0
  246. package/core/workspace-app/tests/unit/elevenlabs.test.js +453 -0
  247. package/core/workspace-app/tests/unit/event-log.test.js +102 -0
  248. package/core/workspace-app/tests/unit/favicon.test.js +68 -0
  249. package/core/workspace-app/tests/unit/files-browser.test.js +224 -0
  250. package/core/workspace-app/tests/unit/git-info.test.js +26 -0
  251. package/core/workspace-app/tests/unit/grid-and-list.test.js +103 -0
  252. package/core/workspace-app/tests/unit/host-tool-wrapper.test.js +18 -0
  253. package/core/workspace-app/tests/unit/html-patterns.test.js +51 -0
  254. package/core/workspace-app/tests/unit/ide-infra.test.js +28 -0
  255. package/core/workspace-app/tests/unit/images-ai.test.js +201 -0
  256. package/core/workspace-app/tests/unit/images-create.test.js +59 -0
  257. package/core/workspace-app/tests/unit/images-providers.test.js +248 -0
  258. package/core/workspace-app/tests/unit/images-svg.test.js +213 -0
  259. package/core/workspace-app/tests/unit/images-workspace.test.js +144 -0
  260. package/core/workspace-app/tests/unit/jobs-fragment.test.js +66 -0
  261. package/core/workspace-app/tests/unit/media-ux.test.js +164 -0
  262. package/core/workspace-app/tests/unit/modal-fit.test.js +87 -0
  263. package/core/workspace-app/tests/unit/modules-load.test.js +23 -0
  264. package/core/workspace-app/tests/unit/narration.test.js +186 -0
  265. package/core/workspace-app/tests/unit/navigation-editor.test.js +260 -0
  266. package/core/workspace-app/tests/unit/navigation.test.js +254 -0
  267. package/core/workspace-app/tests/unit/new-folder.test.js +121 -0
  268. package/core/workspace-app/tests/unit/new-screen-form.test.js +149 -0
  269. package/core/workspace-app/tests/unit/node-menu.test.js +161 -0
  270. package/core/workspace-app/tests/unit/notes.test.js +128 -0
  271. package/core/workspace-app/tests/unit/page-structure.test.js +40 -0
  272. package/core/workspace-app/tests/unit/page-width.test.js +59 -0
  273. package/core/workspace-app/tests/unit/php-version.test.js +90 -0
  274. package/core/workspace-app/tests/unit/plan-card.test.js +44 -0
  275. package/core/workspace-app/tests/unit/plugin-catalog.test.js +31 -0
  276. package/core/workspace-app/tests/unit/plugin-system.test.js +251 -0
  277. package/core/workspace-app/tests/unit/plugins-page.test.js +28 -0
  278. package/core/workspace-app/tests/unit/plugins.test.js +58 -0
  279. package/core/workspace-app/tests/unit/preconditions.test.js +21 -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 +93 -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 +220 -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 +436 -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/package.json +38 -36
  336. /package/{WORKSPACES.md → core/workspace-app/WORKSPACES.md} +0 -0
  337. /package/{actions → core/workspace-app/actions}/ai-items.js +0 -0
  338. /package/{actions → core/workspace-app/actions}/audio.js +0 -0
  339. /package/{actions → core/workspace-app/actions}/build-order.js +0 -0
  340. /package/{actions → core/workspace-app/actions}/checks.js +0 -0
  341. /package/{actions → core/workspace-app/actions}/claude-session.js +0 -0
  342. /package/{actions → core/workspace-app/actions}/commands-center.js +0 -0
  343. /package/{actions → core/workspace-app/actions}/composer.js +0 -0
  344. /package/{actions → core/workspace-app/actions}/ddev-addons.js +0 -0
  345. /package/{actions → core/workspace-app/actions}/ddev-mail.js +0 -0
  346. /package/{actions → core/workspace-app/actions}/folders.js +0 -0
  347. /package/{actions → core/workspace-app/actions}/images-svg.js +0 -0
  348. /package/{actions → core/workspace-app/actions}/images.js +0 -0
  349. /package/{actions → core/workspace-app/actions}/navigation.js +0 -0
  350. /package/{actions → core/workspace-app/actions}/node.js +0 -0
  351. /package/{actions → core/workspace-app/actions}/notes.js +0 -0
  352. /package/{actions → core/workspace-app/actions}/plugins.js +0 -0
  353. /package/{actions → core/workspace-app/actions}/project-make.js +0 -0
  354. /package/{actions → core/workspace-app/actions}/row-actions.js +0 -0
  355. /package/{actions → core/workspace-app/actions}/screens.js +0 -0
  356. /package/{actions → core/workspace-app/actions}/video-edit.js +0 -0
  357. /package/{ai-inventory.js → core/workspace-app/ai-inventory.js} +0 -0
  358. /package/{ai-items.js → core/workspace-app/ai-items.js} +0 -0
  359. /package/{assistant-cast.js → core/workspace-app/assistant-cast.js} +0 -0
  360. /package/{assistant-suggestions.js → core/workspace-app/assistant-suggestions.js} +0 -0
  361. /package/{audio-voice.js → core/workspace-app/audio-voice.js} +0 -0
  362. /package/{auth-google.js → core/workspace-app/auth-google.js} +0 -0
  363. /package/{build-ai.js → core/workspace-app/build-ai.js} +0 -0
  364. /package/{build-order-page.js → core/workspace-app/build-order-page.js} +0 -0
  365. /package/{build-order.js → core/workspace-app/build-order.js} +0 -0
  366. /package/{builder-args.js → core/workspace-app/builder-args.js} +0 -0
  367. /package/{changelog.js → core/workspace-app/changelog.js} +0 -0
  368. /package/{chat-threads.js → core/workspace-app/chat-threads.js} +0 -0
  369. /package/{checks.js → core/workspace-app/checks.js} +0 -0
  370. /package/{claude-args.js → core/workspace-app/claude-args.js} +0 -0
  371. /package/{claude-sessions.js → core/workspace-app/claude-sessions.js} +0 -0
  372. /package/{command-author.js → core/workspace-app/command-author.js} +0 -0
  373. /package/{command-block.js → core/workspace-app/command-block.js} +0 -0
  374. /package/{commands → core/workspace-app/commands}/defaults.yml +0 -0
  375. /package/{commands-page.js → core/workspace-app/commands-page.js} +0 -0
  376. /package/{commands.js → core/workspace-app/commands.js} +0 -0
  377. /package/{cucumber.js → core/workspace-app/cucumber.js} +0 -0
  378. /package/{ddev-addons-page.js → core/workspace-app/ddev-addons-page.js} +0 -0
  379. /package/{ddev-addons.js → core/workspace-app/ddev-addons.js} +0 -0
  380. /package/{ddev-custom-commands.js → core/workspace-app/ddev-custom-commands.js} +0 -0
  381. /package/{ddev-domains.js → core/workspace-app/ddev-domains.js} +0 -0
  382. /package/{ddev-import.js → core/workspace-app/ddev-import.js} +0 -0
  383. /package/{ddev-mail-page.js → core/workspace-app/ddev-mail-page.js} +0 -0
  384. /package/{ddev-mail.js → core/workspace-app/ddev-mail.js} +0 -0
  385. /package/{ddev-options.js → core/workspace-app/ddev-options.js} +0 -0
  386. /package/{ddev-snapshots.js → core/workspace-app/ddev-snapshots.js} +0 -0
  387. /package/{ddev.js → core/workspace-app/ddev.js} +0 -0
  388. /package/{docs → core/workspace-app/docs}/DEVELOPER-GUIDE.md +0 -0
  389. /package/{docs → core/workspace-app/docs}/README.md +0 -0
  390. /package/{docs → core/workspace-app/docs}/REFERENCE.md +0 -0
  391. /package/{docs → core/workspace-app/docs}/USER-GUIDE.md +0 -0
  392. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-board.png +0 -0
  393. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-coffee.png +0 -0
  394. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-colours.png +0 -0
  395. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-icons.png +0 -0
  396. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-moves.png +0 -0
  397. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-pick-cookieman.png +0 -0
  398. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-settings-voices.png +0 -0
  399. /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-status.png +0 -0
  400. /package/{docs → core/workspace-app/docs}/screenshots/create-card/create-card.png +0 -0
  401. /package/{docs → core/workspace-app/docs}/screenshots/create-card/start-ai.png +0 -0
  402. /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-address.png +0 -0
  403. /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-worktree-off.png +0 -0
  404. /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-worktree-on.png +0 -0
  405. /package/{docs → core/workspace-app/docs}/screenshots/guide-ddev-menu.png +0 -0
  406. /package/{docs → core/workspace-app/docs}/screenshots/guide-domains.png +0 -0
  407. /package/{docs → core/workspace-app/docs}/screenshots/guide-home.png +0 -0
  408. /package/{docs → core/workspace-app/docs}/screenshots/guide-ide-menu.png +0 -0
  409. /package/{docs → core/workspace-app/docs}/screenshots/guide-remove-refusal.png +0 -0
  410. /package/{docs → core/workspace-app/docs}/screenshots/guide-rows-worktree.png +0 -0
  411. /package/{docs → core/workspace-app/docs}/screenshots/guide-screens.png +0 -0
  412. /package/{docs → core/workspace-app/docs}/screenshots/guide-settings.png +0 -0
  413. /package/{docs → core/workspace-app/docs}/screenshots/guide-workspace-page.png +0 -0
  414. /package/{docs → core/workspace-app/docs}/screenshots/home/card-settings.png +0 -0
  415. /package/{docs → core/workspace-app/docs}/screenshots/home/drag-reorder.png +0 -0
  416. /package/{docs → core/workspace-app/docs}/screenshots/home/edit-card.png +0 -0
  417. /package/{docs → core/workspace-app/docs}/screenshots/images/claude-code-image.png +0 -0
  418. /package/{docs → core/workspace-app/docs}/screenshots/images/edit-with-ai.png +0 -0
  419. /package/{docs → core/workspace-app/docs}/screenshots/images/gallery.png +0 -0
  420. /package/{docs → core/workspace-app/docs}/screenshots/images/generate-description.png +0 -0
  421. /package/{docs → core/workspace-app/docs}/screenshots/images/generate-plan.png +0 -0
  422. /package/{docs → core/workspace-app/docs}/screenshots/images/providers.png +0 -0
  423. /package/{docs → core/workspace-app/docs}/screenshots/images/svg-form.png +0 -0
  424. /package/{docs → core/workspace-app/docs}/screenshots/images/svg-result.png +0 -0
  425. /package/{docs → core/workspace-app/docs}/screenshots/images/upload.png +0 -0
  426. /package/{docs → core/workspace-app/docs}/screenshots/new-folder/2-form.png +0 -0
  427. /package/{docs → core/workspace-app/docs}/screenshots/new-folder/8-ddev-options.png +0 -0
  428. /package/{docs → core/workspace-app/docs}/screenshots/new-folder/files-folder.png +0 -0
  429. /package/{docs → core/workspace-app/docs}/screenshots/projects/ddev-custom-commands.png +0 -0
  430. /package/{docs → core/workspace-app/docs}/screenshots/projects/state-chip-stop.png +0 -0
  431. /package/{docs → core/workspace-app/docs}/screenshots/readme/dashboard-dark.png +0 -0
  432. /package/{docs → core/workspace-app/docs}/screenshots/readme/dashboard-light.png +0 -0
  433. /package/{docs → core/workspace-app/docs}/screenshots/readme/dev-dark.png +0 -0
  434. /package/{docs → core/workspace-app/docs}/screenshots/readme/dev-light.png +0 -0
  435. /package/{docs → core/workspace-app/docs}/screenshots/readme/docs-dark.png +0 -0
  436. /package/{docs → core/workspace-app/docs}/screenshots/readme/docs-light.png +0 -0
  437. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/ai.svg +0 -0
  438. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/code.svg +0 -0
  439. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/docs.svg +0 -0
  440. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/services.svg +0 -0
  441. /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/sites.svg +0 -0
  442. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-report-dark.png +0 -0
  443. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-report-light.png +0 -0
  444. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-scenarios.png +0 -0
  445. /package/{docs → core/workspace-app/docs}/screenshots/readme/test-steps.png +0 -0
  446. /package/{docs → core/workspace-app/docs}/screenshots/readme/videos-dark.png +0 -0
  447. /package/{docs → core/workspace-app/docs}/screenshots/readme/videos-light.png +0 -0
  448. /package/{docs → core/workspace-app/docs}/screenshots/screens-menu.png +0 -0
  449. /package/{docs → core/workspace-app/docs}/screenshots/screens-new.png +0 -0
  450. /package/{docs → core/workspace-app/docs}/screenshots/settings/auth-google-lists.png +0 -0
  451. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-1-row.png +0 -0
  452. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-2-confirm.png +0 -0
  453. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-3-type-name.png +0 -0
  454. /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-4-type-yes.png +0 -0
  455. /package/{event-log.js → core/workspace-app/event-log.js} +0 -0
  456. /package/{files-browser.js → core/workspace-app/files-browser.js} +0 -0
  457. /package/{folders.js → core/workspace-app/folders.js} +0 -0
  458. /package/{format.js → core/workspace-app/format.js} +0 -0
  459. /package/{github-auth.js → core/workspace-app/github-auth.js} +0 -0
  460. /package/{html.js → core/workspace-app/html.js} +0 -0
  461. /package/{icons.js → core/workspace-app/icons.js} +0 -0
  462. /package/{images-ai.js → core/workspace-app/images-ai.js} +0 -0
  463. /package/{images-create.js → core/workspace-app/images-create.js} +0 -0
  464. /package/{images-page.js → core/workspace-app/images-page.js} +0 -0
  465. /package/{images-records.js → core/workspace-app/images-records.js} +0 -0
  466. /package/{images-render.js → core/workspace-app/images-render.js} +0 -0
  467. /package/{images-svg-core.js → core/workspace-app/images-svg-core.js} +0 -0
  468. /package/{images-svg-sanitize.js → core/workspace-app/images-svg-sanitize.js} +0 -0
  469. /package/{images-svg.js → core/workspace-app/images-svg.js} +0 -0
  470. /package/{images.js → core/workspace-app/images.js} +0 -0
  471. /package/{jobs.js → core/workspace-app/jobs.js} +0 -0
  472. /package/{linger.js → core/workspace-app/linger.js} +0 -0
  473. /package/{lists.js → core/workspace-app/lists.js} +0 -0
  474. /package/{media-info.js → core/workspace-app/media-info.js} +0 -0
  475. /package/{navigation-page.js → core/workspace-app/navigation-page.js} +0 -0
  476. /package/{navigation.js → core/workspace-app/navigation.js} +0 -0
  477. /package/{notes.js → core/workspace-app/notes.js} +0 -0
  478. /package/{palette-index.js → core/workspace-app/palette-index.js} +0 -0
  479. /package/{playwright.config.js → core/workspace-app/playwright.config.js} +0 -0
  480. /package/{plugin-install.js → core/workspace-app/plugin-install.js} +0 -0
  481. /package/{plugin-loader.js → core/workspace-app/plugin-loader.js} +0 -0
  482. /package/{plugin-manifest.js → core/workspace-app/plugin-manifest.js} +0 -0
  483. /package/{plugin-settings.js → core/workspace-app/plugin-settings.js} +0 -0
  484. /package/{plugins → core/workspace-app/plugins}/workspace-notes/index.js +0 -0
  485. /package/{project-make.js → core/workspace-app/project-make.js} +0 -0
  486. /package/{project-name.js → core/workspace-app/project-name.js} +0 -0
  487. /package/{public → core/workspace-app/public}/assistant-avatar-moves.js +0 -0
  488. /package/{public → core/workspace-app/public}/assistant-avatar-ui.js +0 -0
  489. /package/{public → core/workspace-app/public}/assistant-avatar.js +0 -0
  490. /package/{public → core/workspace-app/public}/avatar/druplicon.vrm +0 -0
  491. /package/{public → core/workspace-app/public}/css/base.css +0 -0
  492. /package/{public → core/workspace-app/public}/css/components/ai-assistant.css +0 -0
  493. /package/{public → core/workspace-app/public}/css/components/assistant-avatar.css +0 -0
  494. /package/{public → core/workspace-app/public}/css/components/assistant-views.css +0 -0
  495. /package/{public → core/workspace-app/public}/css/components/build-order.css +0 -0
  496. /package/{public → core/workspace-app/public}/css/components/clone-as-a-git-worktree.css +0 -0
  497. /package/{public → core/workspace-app/public}/css/components/command-palette.css +0 -0
  498. /package/{public → core/workspace-app/public}/css/components/commands-center.css +0 -0
  499. /package/{public → core/workspace-app/public}/css/components/confirm-dialog.css +0 -0
  500. /package/{public → core/workspace-app/public}/css/components/create-card.css +0 -0
  501. /package/{public → core/workspace-app/public}/css/components/ddev-menu.css +0 -0
  502. /package/{public → core/workspace-app/public}/css/components/deep-chat.css +0 -0
  503. /package/{public → core/workspace-app/public}/css/components/density.css +0 -0
  504. /package/{public → core/workspace-app/public}/css/components/floating-assistant-workspace-pages.css +0 -0
  505. /package/{public → core/workspace-app/public}/css/components/folders.css +0 -0
  506. /package/{public → core/workspace-app/public}/css/components/full-height-left-assistant-sidebar-all-pages.css +0 -0
  507. /package/{public → core/workspace-app/public}/css/components/full-height-right-actions-rail-pages-that-have-actions.css +0 -0
  508. /package/{public → core/workspace-app/public}/css/components/grid-and-list.css +0 -0
  509. /package/{public → core/workspace-app/public}/css/components/help-page.css +0 -0
  510. /package/{public → core/workspace-app/public}/css/components/hero.css +0 -0
  511. /package/{public → core/workspace-app/public}/css/components/images.css +0 -0
  512. /package/{public → core/workspace-app/public}/css/components/layout-rhythm-and-spacing-polish.css +0 -0
  513. /package/{public → core/workspace-app/public}/css/components/mic-on-any-field-that-wants-typing.css +0 -0
  514. /package/{public → core/workspace-app/public}/css/components/modal-fit.css +0 -0
  515. /package/{public → core/workspace-app/public}/css/components/page-width.css +0 -0
  516. /package/{public → core/workspace-app/public}/css/components/plugins-page.css +0 -0
  517. /package/{public → core/workspace-app/public}/css/components/project-cards.css +0 -0
  518. /package/{public → core/workspace-app/public}/css/components/project-rows.css +0 -0
  519. /package/{public → core/workspace-app/public}/css/components/reordering-the-workspace-cards.css +0 -0
  520. /package/{public → core/workspace-app/public}/css/components/responsive-home-and-the-workspace-pages.css +0 -0
  521. /package/{public → core/workspace-app/public}/css/components/running-commands-as-toasts.css +0 -0
  522. /package/{public → core/workspace-app/public}/css/components/settings-editor-core-config-yml.css +0 -0
  523. /package/{public → core/workspace-app/public}/css/components/short-results-as-a-toast-at-the-top.css +0 -0
  524. /package/{public → core/workspace-app/public}/css/components/sound-player.css +0 -0
  525. /package/{public → core/workspace-app/public}/css/components/suggest-agents-skills-commands.css +0 -0
  526. /package/{public → core/workspace-app/public}/css/components/tab-modal.css +0 -0
  527. /package/{public → core/workspace-app/public}/css/components/terminals.css +0 -0
  528. /package/{public → core/workspace-app/public}/css/components/test-report-media-one-file-per-row-paged.css +0 -0
  529. /package/{public → core/workspace-app/public}/css/components/top-toolbar-logo-breadcrumb.css +0 -0
  530. /package/{public → core/workspace-app/public}/css/components/video-edit.css +0 -0
  531. /package/{public → core/workspace-app/public}/css/components/web-ide-open-with.css +0 -0
  532. /package/{public → core/workspace-app/public}/css/components/working-indicators.css +0 -0
  533. /package/{public → core/workspace-app/public}/css/components/workspace-cards.css +0 -0
  534. /package/{public → core/workspace-app/public}/favicon.ico +0 -0
  535. /package/{public → core/workspace-app/public}/favicon.png +0 -0
  536. /package/{public → core/workspace-app/public}/htmx.min.js +0 -0
  537. /package/{public → core/workspace-app/public}/logo.png +0 -0
  538. /package/{public → core/workspace-app/public}/media-player.js +0 -0
  539. /package/{public → core/workspace-app/public}/mic.js +0 -0
  540. /package/{public → core/workspace-app/public}/narration.js +0 -0
  541. /package/{public → core/workspace-app/public}/palette.js +0 -0
  542. /package/{public → core/workspace-app/public}/sortable.js +0 -0
  543. /package/{public → core/workspace-app/public}/suggest.js +0 -0
  544. /package/{public → core/workspace-app/public}/terminal.js +0 -0
  545. /package/{public → core/workspace-app/public}/themes/default/theme.css +0 -0
  546. /package/{public → core/workspace-app/public}/themes/ember/theme.css +0 -0
  547. /package/{public → core/workspace-app/public}/themes/press/theme.css +0 -0
  548. /package/{public → core/workspace-app/public}/themes/slate/theme.css +0 -0
  549. /package/{public → core/workspace-app/public}/touch-icon.png +0 -0
  550. /package/{public → core/workspace-app/public}/ui.js +0 -0
  551. /package/{public → core/workspace-app/public}/vendor/ace/LICENSE.txt +0 -0
  552. /package/{public → core/workspace-app/public}/vendor/ace/ace.js +0 -0
  553. /package/{public → core/workspace-app/public}/vendor/ace/ext-language_tools.js +0 -0
  554. /package/{public → core/workspace-app/public}/vendor/ace/ext-searchbox.js +0 -0
  555. /package/{public → core/workspace-app/public}/vendor/ace/mode-css.js +0 -0
  556. /package/{public → core/workspace-app/public}/vendor/ace/mode-html.js +0 -0
  557. /package/{public → core/workspace-app/public}/vendor/ace/mode-javascript.js +0 -0
  558. /package/{public → core/workspace-app/public}/vendor/ace/mode-markdown.js +0 -0
  559. /package/{public → core/workspace-app/public}/vendor/ace/mode-sh.js +0 -0
  560. /package/{public → core/workspace-app/public}/vendor/ace/mode-yaml.js +0 -0
  561. /package/{public → core/workspace-app/public}/vendor/ace/theme-textmate.js +0 -0
  562. /package/{public → core/workspace-app/public}/vendor/ace/theme-tomorrow_night.js +0 -0
  563. /package/{public → core/workspace-app/public}/vendor/ace/worker-yaml.js +0 -0
  564. /package/{public → core/workspace-app/public}/vendor/deep-chat.LICENSE +0 -0
  565. /package/{public → core/workspace-app/public}/vendor/deep-chat.bundle.js +0 -0
  566. /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-LICENSE.txt +0 -0
  567. /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-latin-400-italic.woff2 +0 -0
  568. /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-latin-400-normal.woff2 +0 -0
  569. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-LICENSE.txt +0 -0
  570. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-400-italic.woff2 +0 -0
  571. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-400-normal.woff2 +0 -0
  572. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-600-italic.woff2 +0 -0
  573. /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-600-normal.woff2 +0 -0
  574. /package/{public → core/workspace-app/public}/vendor/fonts/jetbrains-mono-LICENSE.txt +0 -0
  575. /package/{public → core/workspace-app/public}/vendor/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  576. /package/{public → core/workspace-app/public}/vendor/fonts/ubuntu-mono-LICENSE.txt +0 -0
  577. /package/{public → core/workspace-app/public}/vendor/fonts/ubuntu-mono-latin-wght-normal.woff2 +0 -0
  578. /package/{public → core/workspace-app/public}/vendor/tabler-LICENSE.txt +0 -0
  579. /package/{public → core/workspace-app/public}/vendor/tabler-sprite.svg +0 -0
  580. /package/{public → core/workspace-app/public}/vendor/three/LICENSE +0 -0
  581. /package/{public → core/workspace-app/public}/vendor/three/addons/loaders/GLTFLoader.js +0 -0
  582. /package/{public → core/workspace-app/public}/vendor/three/addons/utils/BufferGeometryUtils.js +0 -0
  583. /package/{public → core/workspace-app/public}/vendor/three/addons/utils/SkeletonUtils.js +0 -0
  584. /package/{public → core/workspace-app/public}/vendor/three/three.core.min.js +0 -0
  585. /package/{public → core/workspace-app/public}/vendor/three/three.module.min.js +0 -0
  586. /package/{public → core/workspace-app/public}/vendor/three-vrm/LICENSE +0 -0
  587. /package/{public → core/workspace-app/public}/vendor/three-vrm/three-vrm.module.min.js +0 -0
  588. /package/{public → core/workspace-app/public}/vendor/uikit-icons.min.js +0 -0
  589. /package/{public → core/workspace-app/public}/vendor/uikit.min.css +0 -0
  590. /package/{public → core/workspace-app/public}/vendor/uikit.min.js +0 -0
  591. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/bookmark-outlined.svg +0 -0
  592. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/headset.svg +0 -0
  593. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/menu.svg +0 -0
  594. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/pause.svg +0 -0
  595. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/play.svg +0 -0
  596. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/share.svg +0 -0
  597. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/sound-off.svg +0 -0
  598. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/sound-on.svg +0 -0
  599. /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/stop.svg +0 -0
  600. /package/{public → core/workspace-app/public}/vendor/xterm/addon-fit.js +0 -0
  601. /package/{public → core/workspace-app/public}/vendor/xterm/addon-unicode11.js +0 -0
  602. /package/{public → core/workspace-app/public}/vendor/xterm/addon-web-links.js +0 -0
  603. /package/{public → core/workspace-app/public}/vendor/xterm/xterm.css +0 -0
  604. /package/{public → core/workspace-app/public}/vendor/xterm/xterm.js +0 -0
  605. /package/{rag.js → core/workspace-app/rag.js} +0 -0
  606. /package/{reader.js → core/workspace-app/reader.js} +0 -0
  607. /package/{repo-blobs.js → core/workspace-app/repo-blobs.js} +0 -0
  608. /package/{report-pdf.js → core/workspace-app/report-pdf.js} +0 -0
  609. /package/{row-layout.js → core/workspace-app/row-layout.js} +0 -0
  610. /package/{screenrc → core/workspace-app/screenrc} +0 -0
  611. /package/{screens.js → core/workspace-app/screens.js} +0 -0
  612. /package/{scripts → core/workspace-app/scripts}/code-server-start.sh +0 -0
  613. /package/{scripts → core/workspace-app/scripts}/images-claude-finish.js +0 -0
  614. /package/{scripts → core/workspace-app/scripts}/screen-host.sh +0 -0
  615. /package/{scripts → core/workspace-app/scripts}/screen-runner.js +0 -0
  616. /package/{scripts → core/workspace-app/scripts}/screen-then.js +0 -0
  617. /package/{scripts → core/workspace-app/scripts}/update.sh +0 -0
  618. /package/{settings-file.js → core/workspace-app/settings-file.js} +0 -0
  619. /package/{speech.js → core/workspace-app/speech.js} +0 -0
  620. /package/{tab-modal.js → core/workspace-app/tab-modal.js} +0 -0
  621. /package/{test-reports.js → core/workspace-app/test-reports.js} +0 -0
  622. /package/{themes.js → core/workspace-app/themes.js} +0 -0
  623. /package/{transcripts.js → core/workspace-app/transcripts.js} +0 -0
  624. /package/{urls.js → core/workspace-app/urls.js} +0 -0
  625. /package/{validate.js → core/workspace-app/validate.js} +0 -0
  626. /package/{version-check.js → core/workspace-app/version-check.js} +0 -0
  627. /package/{video-edit.js → core/workspace-app/video-edit.js} +0 -0
  628. /package/{waiting.js → core/workspace-app/waiting.js} +0 -0
  629. /package/{workspace-card-tools.js → core/workspace-app/workspace-card-tools.js} +0 -0
  630. /package/{workspace-delete.js → core/workspace-app/workspace-delete.js} +0 -0
  631. /package/{workspace-types.js → core/workspace-app/workspace-types.js} +0 -0
  632. /package/{workspaces.js → core/workspace-app/workspaces.js} +0 -0
@@ -0,0 +1,621 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Agents, skills and prompts live in three places, and this file is how they
4
+ # travel between them:
5
+ #
6
+ # 1. The workspace folder ~/workspace/{agents,skills,prompts}/
7
+ # Drafts and work in progress, editable from the dashboard.
8
+ # 2. The Claude Code CLI ~/.claude/{agents,skills,commands}/
9
+ # What an actual session loads.
10
+ # 3. The shared repo github.com/webship/ai-agents
11
+ # What the team has, installed there with its own ./sync.sh.
12
+ #
13
+ # Direction matters:
14
+ # pull repo -> workspace folder (get what the team has)
15
+ # fetch repo -> ~/.claude (use it in sessions now)
16
+ # install workspace -> ~/.claude (use your draft now; existing verb)
17
+ # promote workspace -> a PR on the repo (share it; that is the agents' job,
18
+ # via github-issue-manager / drupal-mr-manager — never automatic)
19
+
20
+ # AI_ITEMS_REPO / _REF are resolved in bootstrap.sh from settings.yml
21
+ # `sources.ai_items`, because this file is sourced before settings.yml is parsed.
22
+
23
+ # Where each kind lives, per source. `prompts` is `commands` in the CLI, and in the
24
+ # shared repo it is a top-level `prompts/` beside the agents and skills it invokes —
25
+ # so the repo path and the project path are two different answers, below.
26
+ function ai_items_repo_path() {
27
+ case "$1" in
28
+ ai-agents) echo ".claude/agents" ;;
29
+ ai-skills) echo ".claude/skills" ;;
30
+ ai-commands) echo ".claude/commands" ;;
31
+ ai-prompts) echo "prompts" ;;
32
+ *) return 1 ;;
33
+ esac
34
+ }
35
+
36
+ # Where each kind installs INSIDE A PROJECT: always that project's own .claude/,
37
+ # which is what a Claude Code session started there reads. Never the repo layout —
38
+ # a project's prompts belong in .claude/commands, not in a prompts/ directory.
39
+ function ai_items_project_path() {
40
+ case "$1" in
41
+ ai-agents) echo ".claude/agents" ;;
42
+ ai-skills) echo ".claude/skills" ;;
43
+ ai-commands) echo ".claude/commands" ;;
44
+ ai-prompts) echo ".claude/commands" ;;
45
+ *) return 1 ;;
46
+ esac
47
+ }
48
+
49
+ function ai_items_claude_path() {
50
+ case "$1" in
51
+ ai-agents) echo "${HOME}/.claude/agents" ;;
52
+ ai-skills) echo "${HOME}/.claude/skills" ;;
53
+ ai-commands) echo "${HOME}/.claude/commands" ;;
54
+ ai-prompts) echo "${HOME}/.claude/commands" ;;
55
+ *) return 1 ;;
56
+ esac
57
+ }
58
+
59
+ # Where an install actually lands: globally in ~/.claude, or inside one project's own .claude/,
60
+ # which Claude Code reads the same way — a session started under that project sees it, one started
61
+ # elsewhere does not. `to` is empty for global, or "<workspace>/<project>" for a project.
62
+ function ai_items_install_path() {
63
+ local kind="$1" to="$2" ws project subdir;
64
+ if [ -z "${to}" ] || [ "${to}" == 'claude' ] || [ "${to}" == '_none_' ] ; then
65
+ ai_items_claude_path "${kind}" ;
66
+ return $? ;
67
+ fi
68
+ ws="${to%%/*}"; project="${to#*/}";
69
+ if [ "${ws}" == "${to}" ] || [ -z "${project}" ] ; then
70
+ echo "'--to ${to}' needs a workspace and a project: --to dev/myproject." >&2 ;
71
+ return 1 ;
72
+ fi
73
+ if [ ! -d "${WORKSPACE_ROOT}/${ws}/${project}" ] ; then
74
+ echo "No ${ws}/${project}." >&2 ;
75
+ return 1 ;
76
+ fi
77
+ subdir=$(ai_items_project_path "${kind}") || return 1 ; # ".claude/agents" etc.
78
+ echo "${WORKSPACE_ROOT}/${ws}/${project}/${subdir}" ;
79
+ }
80
+
81
+ # Same character set clone_ai_item already enforces for a new name. `name` also
82
+ # becomes a path component in install_ai_item/ai_items_fetch, so it gets the same
83
+ # check before any copy runs.
84
+ function ai_items_sanitize_name() {
85
+ local name="$1";
86
+ if [ -z "${name}" ] || [ "${name}" == '_all_' ] ; then
87
+ echo "${name}"; return 0 ;
88
+ fi
89
+ if ! echo "${name}" | grep -qE '^[a-zA-Z0-9_-]+$' ; then
90
+ echo "Invalid name '${name}': letters, digits, hyphen and underscore only." >&2 ;
91
+ return 1 ;
92
+ fi
93
+ echo "${name}";
94
+ }
95
+
96
+ # gh does the authenticated reads; this is a private repository.
97
+ #
98
+ # Gate on the ACTIVE account, not on `gh auth status`: that command exits
99
+ # non-zero when ANY configured account fails, not when the one in use does. The
100
+ # dashboard container mounts ~/.config/gh but cannot read the host keyring the
101
+ # token is stored in, so it always carries a dead "default" entry alongside a
102
+ # perfectly good GH_TOKEN — and the old check refused every repo read there,
103
+ # reporting the CLI as unauthenticated while `gh api user` answered fine.
104
+ function ensure_gh_cli() {
105
+ if ! command -v gh > /dev/null 2>&1 ; then
106
+ echo "The GitHub CLI is needed to read ${AI_ITEMS_REPO} (private).";
107
+ echo " install: https://cli.github.com then: gh auth login";
108
+ return 1 ;
109
+ fi
110
+ # Make a real authenticated call rather than asking gh to self-report.
111
+ #
112
+ # `gh auth status` exits non-zero when ANY configured account fails, so it
113
+ # cannot be used. `gh auth status --active` fixes that but only exists in newer
114
+ # gh: Ubuntu still ships 2.45, where the flag is "unknown flag: --active" and
115
+ # every check would fail. `gh api user` behaves the same on every version and
116
+ # proves the credential actually works, which is the thing being tested.
117
+ # gh's own login first, when settings.yml allows it: on a developer machine gh is already
118
+ # signed in and there is no reason to hold a second credential. Inside the dashboard
119
+ # container it is not — gh reads the same mounted ~/.config/gh, but the token itself lives
120
+ # in the host keyring, which the container cannot read.
121
+ # The GitHub plugin (plugin.github.settings.yml) owns these once it is installed; the settings.yml block is the older home.
122
+ local use_cli="${plugin_github_use_gh_cli:-${git_github_use_gh_cli}}" token="${plugin_github_token:-${git_github_token}}";
123
+ if [ ! "${use_cli}" == 'false' ] && gh api user --jq .login > /dev/null 2>&1 ; then
124
+ return 0 ;
125
+ fi
126
+
127
+ # Then the token from settings.yml. An environment variable still wins over both, and the
128
+ # value is never echoed.
129
+ if [ -z "${GH_TOKEN}" ] && [ -z "${GITHUB_TOKEN}" ] \
130
+ && [ -n "${token}" ] && [ "${token}" != 'CHANGE_ME' ]; then
131
+ export GH_TOKEN="${token}" ;
132
+ fi
133
+ if gh api user --jq .login > /dev/null 2>&1 ; then
134
+ return 0 ;
135
+ fi
136
+ echo "The GitHub CLI is installed but has no working account for ${AI_ITEMS_REPO} (private).";
137
+ echo "";
138
+ if [ -n "${IS_DDEV_PROJECT}" ]; then
139
+ # Inside the dashboard container. `gh auth status` here reports the account from the
140
+ # mounted ~/.config/gh and then calls its token invalid, because the token itself lives
141
+ # in the host keyring, which this container cannot read.
142
+ echo " You are inside the dashboard container, which has no token of its own.";
143
+ echo "";
144
+ echo " Run it on the host instead — this works today:";
145
+ echo " cd ${WORKSPACE_ROOT}/${doc_name} && bash cmd-tools-ai-items.sh --pull";
146
+ echo "";
147
+ echo " Or set git.github.token at https://${hub_domain:-workspace.ddev.site}/settings — the";
148
+ echo " dashboard keeps it server-side and this script picks it up with no restart.";
149
+ echo " Prefer a fine-grained token that can only READ ${AI_ITEMS_REPO}: anything";
150
+ echo " running in this container, the AI assistant included, can use whatever you set.";
151
+ else
152
+ echo " Sign in: gh auth login";
153
+ fi
154
+ return 1 ;
155
+ }
156
+
157
+ # Let git authenticate the way gh does, for the one clone/fetch below.
158
+ #
159
+ # A token in the environment is enough for `gh api`, but not for git: without a
160
+ # credential helper `git clone https://github.com/...` of a private repository
161
+ # stops at "could not read Username for 'https://github.com'". Setting the
162
+ # helper through GIT_CONFIG_* keeps it to this process — no token on a command
163
+ # line, in a URL, or written into anyone's ~/.gitconfig.
164
+ function ensure_git_github_auth() {
165
+ export GIT_CONFIG_COUNT=1 ;
166
+ export GIT_CONFIG_KEY_0="credential.https://github.com.helper" ;
167
+ export GIT_CONFIG_VALUE_0="!gh auth git-credential" ;
168
+ }
169
+
170
+ # What the shared repo has, one name per line.
171
+ function ai_items_list_remote() {
172
+ local kind="$1";
173
+ local repo_path;
174
+ repo_path=$(ai_items_repo_path "${kind}") || return 1 ;
175
+ ensure_gh_cli || return 1 ;
176
+
177
+ if [ "${kind}" == 'ai-skills' ] ; then
178
+ gh api "repos/${AI_ITEMS_REPO}/contents/${repo_path}?ref=${AI_ITEMS_REF}" \
179
+ --jq '.[] | select(.type=="dir") | .name' 2>/dev/null ;
180
+ else
181
+ gh api "repos/${AI_ITEMS_REPO}/contents/${repo_path}?ref=${AI_ITEMS_REF}" \
182
+ --jq '.[] | select(.type=="file") | select(.name|endswith(".md")) | select(.name!="README.md") | .name | sub("\\.md$";"")' 2>/dev/null ;
183
+ fi
184
+ }
185
+
186
+ # Show all three sources side by side, so it is obvious what is where.
187
+ function ai_items_status() {
188
+ local kind="${1:-${doc_name}}";
189
+ local claude_dir workspace_dir;
190
+ claude_dir=$(ai_items_claude_path "${kind}") || return 1 ;
191
+ workspace_dir="${WORKSPACE_ROOT}/${kind}";
192
+
193
+ echo "*---------------------------------------------------------------------*";
194
+ echo " ${kind}";
195
+ echo "*---------------------------------------------------------------------*";
196
+ printf " %-46s %s\n" "workspace ${workspace_dir}" "$(ai_items_count_local "${kind}" "${workspace_dir}")";
197
+ # A prompt starts a new session; it is not something ~/.claude loads. Showing that
198
+ # column would imply it might.
199
+ if [ ! "${kind}" == 'ai-prompts' ] ; then
200
+ printf " %-46s %s\n" "claude ${claude_dir}" "$(ai_items_count_local "${kind}" "${claude_dir}")";
201
+ fi
202
+ if ensure_gh_cli > /dev/null 2>&1 ; then
203
+ printf " %-46s %s\n" "repo ${AI_ITEMS_REPO}@${AI_ITEMS_REF}" "$(ai_items_list_remote "${kind}" | wc -l)";
204
+ else
205
+ printf " %-46s %s\n" "repo ${AI_ITEMS_REPO}" "(gh not authenticated)";
206
+ fi
207
+ }
208
+
209
+ function ai_items_count_local() {
210
+ local kind="$1" dir="$2";
211
+ if [ ! -d "${dir}" ]; then echo "0 (missing)"; return 0 ; fi
212
+ if [ "${kind}" == 'ai-skills' ] ; then
213
+ find "${dir}" -mindepth 2 -maxdepth 2 -name 'SKILL.md' 2>/dev/null | wc -l ;
214
+ else
215
+ find "${dir}" -maxdepth 1 -name '*.md' ! -name 'README.md' ! -name 'AGENTS.md' ! -name 'CLAUDE.md' 2>/dev/null | wc -l ;
216
+ fi
217
+ }
218
+
219
+ # Copy one item (or all) out of the shared repo into a destination directory.
220
+ # dest: "claude" (~/.claude — usable in sessions now) or "workspace".
221
+ function ai_items_fetch() {
222
+ local kind="$1" name="$2" dest="$3";
223
+ local repo_path target tmp;
224
+ name=$(ai_items_sanitize_name "${name}") || return 1 ;
225
+ repo_path=$(ai_items_repo_path "${kind}") || return 1 ;
226
+ ensure_gh_cli || return 1 ;
227
+
228
+ if [ "${dest}" == 'workspace' ] ; then
229
+ target="${WORKSPACE_ROOT}/${kind}";
230
+ else
231
+ target=$(ai_items_claude_path "${kind}") || return 1 ;
232
+ fi
233
+ mkdir -p "${target}" ;
234
+
235
+ # Keep the checkout between runs (a fetch beats a clone), and read the files
236
+ # out of the FETCHED REMOTE BRANCH rather than its working tree — so whatever
237
+ # branch that clone sits on, and any work in progress in it, is left alone.
238
+ local checkout="${WORKSPACE_ROOT}/${kind}/dev-ai-agents";
239
+ tmp=$(mktemp -d) ;
240
+ ensure_git_github_auth ;
241
+ if [ -d "${checkout}/.git" ]; then
242
+ echo "Fetching ${AI_ITEMS_REPO}…" ;
243
+ git -C "${checkout}" fetch --quiet origin || { rm -rf "${tmp}"; return 1 ; }
244
+ else
245
+ echo "Cloning ${AI_ITEMS_REPO}…" ;
246
+ git clone --quiet "https://github.com/${AI_ITEMS_REPO}.git" "${checkout}" || {
247
+ echo "Could not clone — check gh auth and repository access." ; rm -rf "${tmp}"; return 1 ;
248
+ }
249
+ fi
250
+
251
+ local remote_branch;
252
+ remote_branch=$(git -C "${checkout}" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null) ;
253
+ remote_branch="${remote_branch:-origin/${AI_ITEMS_REF}}" ;
254
+ git -C "${checkout}" archive "${remote_branch}" "${repo_path}" 2>/dev/null | tar -x -C "${tmp}" || {
255
+ echo "Nothing at ${repo_path} in ${remote_branch}." ; rm -rf "${tmp}"; return 1 ;
256
+ }
257
+
258
+ local source_dir="${tmp}/${repo_path}";
259
+ local copied=0;
260
+ local ref_name="${remote_branch#origin/}";
261
+
262
+ # Record what was fetched in the lock next to `target`, keyed by the git object sha
263
+ # for that path at the fetched ref — already resolvable from the local clone, no
264
+ # extra network call. Only meaningful for dest=claude: that is the only fetch
265
+ # direction Claude Code actually reads from.
266
+ function _ai_items_lock_fetched() {
267
+ local item="$1" path="$2";
268
+ [ "${dest}" == 'claude' ] || return 0 ;
269
+ local sha; sha=$(git -C "${checkout}" rev-parse "${remote_branch}:${path}" 2>/dev/null) ;
270
+ [ -n "${sha}" ] || return 0 ;
271
+ ai_items_lock_set "${target}" "${kind}" "${item}" \
272
+ "${AI_ITEMS_REPO}@${ref_name}" "repo" "${path}" "${sha}" ;
273
+ }
274
+
275
+ if [ -z "${name}" ] || [ "${name}" == '_all_' ] ; then
276
+ if [ "${kind}" == 'ai-skills' ] ; then
277
+ for d in "${source_dir}"/*/ ; do
278
+ [ -f "${d}/SKILL.md" ] || continue ;
279
+ local item; item=$(basename "${d}") ;
280
+ rsync -a --delete "${d}" "${target}/${item}/" && { copied=$((copied+1)); _ai_items_lock_fetched "${item}" "${repo_path}/${item}" ; } ;
281
+ done
282
+ else
283
+ for f in "${source_dir}"/*.md ; do
284
+ case "$(basename "${f}")" in README.md|AGENTS.md|CLAUDE.md) continue ;; esac
285
+ local item; item=$(basename "${f}" .md) ;
286
+ cp "${f}" "${target}/" && { copied=$((copied+1)); _ai_items_lock_fetched "${item}" "${repo_path}/${item}.md" ; } ;
287
+ done
288
+ fi
289
+ echo "Fetched ${copied} ${kind} into ${target}.";
290
+ else
291
+ if [ "${kind}" == 'ai-skills' ] ; then
292
+ if [ ! -f "${source_dir}/${name}/SKILL.md" ]; then
293
+ echo "No skill '${name}' in ${AI_ITEMS_REPO}." ; rm -rf "${tmp}" ; return 1 ;
294
+ fi
295
+ rsync -a --delete "${source_dir}/${name}/" "${target}/${name}/" ;
296
+ _ai_items_lock_fetched "${name}" "${repo_path}/${name}" ;
297
+ else
298
+ if [ ! -f "${source_dir}/${name}.md" ]; then
299
+ echo "No ${kind%s} '${name}' in ${AI_ITEMS_REPO}." ; rm -rf "${tmp}" ; return 1 ;
300
+ fi
301
+ cp "${source_dir}/${name}.md" "${target}/" ;
302
+ _ai_items_lock_fetched "${name}" "${repo_path}/${name}.md" ;
303
+ fi
304
+ echo "Fetched ${name} into ${target}.";
305
+ fi
306
+
307
+ rm -rf "${tmp}" ;
308
+ if [ "${dest}" == 'claude' ] ; then
309
+ echo "Restart your Claude Code session to pick it up." ;
310
+ fi
311
+ }
312
+
313
+ # Install one item (or all) from this workspace folder into ~/.claude, or into one project's own
314
+ # .claude/ when `to` names one (`<workspace>/<project>`) — same file, same frontmatter `name:`
315
+ # Claude Code dispatches on, only the reach differs: global is every session, project-local is only
316
+ # sessions started under that project.
317
+ function install_ai_item() {
318
+ local kind="$1" name="$2" to="${3:-}";
319
+ name=$(ai_items_sanitize_name "${name}") || return 1 ;
320
+ local src="${WORKSPACE_ROOT}/${kind}";
321
+ local target copied=0;
322
+ target=$(ai_items_install_path "${kind}" "${to}") || return 1 ;
323
+ mkdir -p "${target}" ;
324
+
325
+ # Record what was installed from this workspace draft, keyed by a local content
326
+ # hash — no repo involved, so `ai_items_check` recomputes this same hash from
327
+ # the draft's current state instead of calling out anywhere.
328
+ function _ai_items_lock_installed() {
329
+ local item="$1" draft_path="$2";
330
+ local hash; hash=$(ai_items_local_hash "${draft_path}") ;
331
+ [ -n "${hash}" ] || return 0 ;
332
+ ai_items_lock_set "${target}" "${kind}" "${item}" \
333
+ "workspace" "workspace" "${kind}/${item}$([ "${kind}" == 'ai-skills' ] || echo '.md')" "${hash}" ;
334
+ }
335
+
336
+ if [ -z "${name}" ] || [ "${name}" == '_all_' ] ; then
337
+ if [ "${kind}" == 'ai-skills' ] ; then
338
+ for d in "${src}"/*/ ; do
339
+ [ -f "${d}/SKILL.md" ] || continue ;
340
+ local item; item=$(basename "${d}") ;
341
+ rsync -a "${d}" "${target}/${item}/" && { copied=$((copied+1)); _ai_items_lock_installed "${item}" "${d}" ; } ;
342
+ done
343
+ else
344
+ for f in "${src}"/*.md ; do
345
+ case "$(basename "${f}")" in README.md|AGENTS.md|CLAUDE.md) continue ;; esac
346
+ [ -f "${f}" ] || continue ;
347
+ local item; item=$(basename "${f}" .md) ;
348
+ cp "${f}" "${target}/" && { copied=$((copied+1)); _ai_items_lock_installed "${item}" "${f}" ; } ;
349
+ done
350
+ fi
351
+ echo "Installed ${copied} ${kind} into ${target}.";
352
+ elif [ "${kind}" == 'ai-skills' ] ; then
353
+ [ -f "${src}/${name}/SKILL.md" ] || { echo "No skill '${name}' in ${src}." ; return 1 ; }
354
+ rsync -a "${src}/${name}/" "${target}/${name}/" ;
355
+ _ai_items_lock_installed "${name}" "${src}/${name}" ;
356
+ echo "Installed ${name} into ${target}.";
357
+ else
358
+ [ -f "${src}/${name}.md" ] || { echo "No ${kind%s} '${name}' in ${src}." ; return 1 ; }
359
+ cp "${src}/${name}.md" "${target}/" ;
360
+ _ai_items_lock_installed "${name}" "${src}/${name}.md" ;
361
+ echo "Installed ${name} into ${target}.";
362
+ fi
363
+ echo "Restart your Claude Code session to pick it up.";
364
+ }
365
+
366
+ # Clone one item under a new name, so it can be customised for a specific need.
367
+ #
368
+ # A copy is not enough. An agent or skill carries its own name in its YAML
369
+ # frontmatter, in its heading, and often in its prose — Claude Code dispatches
370
+ # on the frontmatter `name`, so two files claiming the same one collide and
371
+ # which definition wins is undefined. Rewriting the identity is the point of
372
+ # this function; `cp` is the part that does not matter.
373
+ #
374
+ # The original is never touched: this only ever writes the new name.
375
+ function clone_ai_item() {
376
+ local kind="$1" name="$2" new_name="$3";
377
+ local dir="${WORKSPACE_ROOT}/${kind}";
378
+
379
+ if [ -z "${new_name}" ] || [ "${new_name}" == '_none_' ] ; then
380
+ echo "Give the copy a name: --clone-to <new-name>." ;
381
+ return 1 ;
382
+ fi
383
+ if [ "${name}" == '_all_' ] || [ -z "${name}" ] ; then
384
+ echo "Name the item to clone: cmd-tools-ai-items.sh <name> --clone-to <new-name>." ;
385
+ return 1 ;
386
+ fi
387
+ # Same character set the dashboard enforces, so a clone made either way is
388
+ # equally safe to write and to install into ~/.claude.
389
+ if ! echo "${new_name}" | grep -qE '^[a-zA-Z0-9_-]+$' ; then
390
+ echo "Invalid name '${new_name}': letters, digits, hyphen and underscore only." ;
391
+ return 1 ;
392
+ fi
393
+
394
+ local src dest;
395
+ if [ "${kind}" == 'ai-skills' ] ; then
396
+ src="${dir}/${name}"; dest="${dir}/${new_name}";
397
+ [ -f "${src}/SKILL.md" ] || { echo "No skill '${name}' in ${dir}." ; return 1 ; }
398
+ [ -e "${dest}" ] && { echo "'${new_name}' already exists — pick another name." ; return 1 ; }
399
+ cp -a "${src}" "${dest}" ;
400
+ rewrite_ai_item_identity "${dest}/SKILL.md" "${name}" "${new_name}" ;
401
+ else
402
+ src="${dir}/${name}.md"; dest="${dir}/${new_name}.md";
403
+ [ -f "${src}" ] || { echo "No ${kind%s} '${name}' in ${dir}." ; return 1 ; }
404
+ [ -e "${dest}" ] && { echo "'${new_name}' already exists — pick another name." ; return 1 ; }
405
+ cp -a "${src}" "${dest}" ;
406
+ rewrite_ai_item_identity "${dest}" "${name}" "${new_name}" ;
407
+ fi
408
+
409
+ echo "Cloned ${name} to ${new_name} in ${dir}." ;
410
+ CLONED_AI_ITEM="${dest}";
411
+ }
412
+
413
+ # Point a cloned file at its own name instead of the original's.
414
+ function rewrite_ai_item_identity() {
415
+ local file="$1" old="$2" new="$3";
416
+ python3 - "${file}" "${old}" "${new}" <<'PYEOF'
417
+ import re, sys
418
+ path, old, new = sys.argv[1], sys.argv[2], sys.argv[3]
419
+ text = open(path).read()
420
+
421
+ # One pass over the whole file, which covers the frontmatter `name:` (what Claude
422
+ # Code dispatches on), the heading, and every prose reference.
423
+ #
424
+ # The boundaries are lookarounds over [\w-], not \b. These names are
425
+ # hyphenated, and \b treats a hyphen as a boundary — so cloning
426
+ # "webship-workspace" to "webship-workspace-client-x" would find the old slug
427
+ # inside the name just written and produce "webship-workspace-client-x-client-x".
428
+ # Requiring that neither side be a word character or a hyphen also leaves a
429
+ # longer sibling name alone.
430
+ pattern = r'(?<![\w-])%s(?![\w-])' % re.escape(old)
431
+ open(path, 'w').write(re.sub(pattern, new, text))
432
+ PYEOF
433
+ }
434
+
435
+ # Hand the clone to Claude Code to tailor and reformat for its new purpose.
436
+ #
437
+ # Kept separate from the copy: cloning must work with no AI and no network, and
438
+ # a failed or absent `claude` may not cost you the clone you already have.
439
+ function tailor_ai_item() {
440
+ local kind="$1" file="$2" new_name="$3" purpose="$4";
441
+
442
+ if [ -z "${purpose}" ] || [ "${purpose}" == '_none_' ] ; then
443
+ return 0 ;
444
+ fi
445
+ if ! command -v claude > /dev/null 2>&1 ; then
446
+ echo "Claude Code CLI not found — the clone is in place, untailored." ;
447
+ echo " install: https://claude.com/claude-code" ;
448
+ return 0 ;
449
+ fi
450
+
451
+ echo "Tailoring ${new_name} for: ${purpose}" ;
452
+ local kind_note;
453
+ case "${kind}" in
454
+ agents) kind_note="a Claude Code subagent definition. Keep the YAML frontmatter valid and keep name: ${new_name}. The description: decides when Claude invokes it, so rewrite it for the new purpose and keep the tools: list to what this agent actually needs." ;;
455
+ skills) kind_note="a Claude Code SKILL.md. Keep the YAML frontmatter valid and keep name: ${new_name}, with a one-line description: saying what it does and when to use it." ;;
456
+ prompts) kind_note="a reusable prompt for a Claude Code slash command. Plain markdown, no frontmatter." ;;
457
+ esac
458
+
459
+ local prompt;
460
+ prompt="Rewrite the file at ${file}. It is ${kind_note}
461
+
462
+ It was cloned from another item and still reads as a generic copy. Tailor it for this purpose: ${purpose}
463
+
464
+ Keep what genuinely transfers, cut what does not apply, and reformat it so it reads as a purpose-built file rather than an edited copy. Do not invent capabilities, commands, or file paths that do not exist — read the repository if you need to check one. Output ONLY the complete new file content: no commentary, no code fences.";
465
+
466
+ local tmp;
467
+ tmp=$(mktemp) ;
468
+ if claude -p "${prompt}" --allowedTools Read Glob Grep --disallowedTools Write Edit Bash \
469
+ --no-session-persistence > "${tmp}" 2>/dev/null && [ -s "${tmp}" ] ; then
470
+ mv "${tmp}" "${file}" ;
471
+ echo "Tailored ${file}." ;
472
+ else
473
+ rm -f "${tmp}" ;
474
+ echo "Claude Code returned nothing — keeping the untailored clone at ${file}." ;
475
+ fi
476
+ }
477
+
478
+ # Hand one draft to the AI agent to file the issue and open the PR on the
479
+ # shared repo — the "promote" direction the header describes, and the only way
480
+ # anything local reaches ${AI_ITEMS_REPO}.
481
+ #
482
+ # Confirm-gated on purpose: filing an issue and opening a pull request are
483
+ # public acts, so without --confirm this prints exactly what it would do and
484
+ # stops.
485
+ function ai_items_propose() {
486
+ local kind="$1" name="$2" summary="$3" confirmed="$4";
487
+ name=$(ai_items_sanitize_name "${name}") || return 1 ;
488
+ if [ -z "${name}" ] || [ "${name}" == '_all_' ] ; then
489
+ echo "Name the ${kind%s} to propose: cmd-tools-ai-items.sh <name> --propose --summary \"…\"" ;
490
+ return 1 ;
491
+ fi
492
+ [ "${summary}" == '_none_' ] && summary="";
493
+
494
+ local src="${WORKSPACE_ROOT}/${kind}" path repo_path item_repo_path;
495
+ repo_path=$(ai_items_repo_path "${kind}") || return 1 ;
496
+ if [ "${kind}" == 'ai-skills' ] ; then
497
+ path="${src}/${name}";
498
+ [ -f "${path}/SKILL.md" ] || { echo "No skill '${name}' in ${src}." ; return 1 ; }
499
+ item_repo_path="${repo_path}/${name}";
500
+ else
501
+ path="${src}/${name}.md";
502
+ [ -f "${path}" ] || { echo "No ${kind%s} '${name}' in ${src}." ; return 1 ; }
503
+ item_repo_path="${repo_path}/${name}.md";
504
+ fi
505
+
506
+ # New to the repo, or a change to what is there — said honestly as unknown
507
+ # when the repository cannot be read (the dashboard container without a token).
508
+ local state="of unknown status against";
509
+ if ensure_gh_cli > /dev/null 2>&1 ; then
510
+ if [ "${kind}" == 'ai-skills' ] ; then
511
+ if gh api "repos/${AI_ITEMS_REPO}/contents/${item_repo_path}?ref=${AI_ITEMS_REF}" > /dev/null 2>&1 ; then
512
+ state="a change to";
513
+ else
514
+ state="new to";
515
+ fi
516
+ else
517
+ local remote_content;
518
+ remote_content=$(gh api "repos/${AI_ITEMS_REPO}/contents/${item_repo_path}?ref=${AI_ITEMS_REF}" --jq '.content' 2>/dev/null | base64 -d 2>/dev/null) ;
519
+ if [ -z "${remote_content}" ] ; then
520
+ state="new to";
521
+ elif [ "${remote_content}" == "$(cat "${path}")" ] ; then
522
+ echo "${kind}/${name} is identical to ${AI_ITEMS_REPO}@${AI_ITEMS_REF} — nothing to propose." ;
523
+ return 0 ;
524
+ else
525
+ state="a change to";
526
+ fi
527
+ fi
528
+ fi
529
+ if [ "${state}" == 'of unknown status against' ] ; then
530
+ echo " (could not read ${AI_ITEMS_REPO} to compare — the agent will work it out)" ;
531
+ fi
532
+
533
+ echo "*---------------------------------------------------------------------*";
534
+ echo " Propose ${kind}/${name} to ${AI_ITEMS_REPO}";
535
+ echo "*---------------------------------------------------------------------*";
536
+ echo " file ${path}";
537
+ echo " status ${state} ${AI_ITEMS_REPO}@${AI_ITEMS_REF}";
538
+ echo " summary ${summary:-'(no summary given)'}";
539
+ echo "";
540
+ echo " The AI agent would: file an issue, push a review branch, open a PR";
541
+ echo " against ${AI_ITEMS_REF}, and leave the human-review boxes unticked.";
542
+ echo " It never merges, and it never pushes to ${AI_ITEMS_REF} directly.";
543
+
544
+ if [ ! "${confirmed}" == 'yes' ] ; then
545
+ echo "";
546
+ echo " Nothing has been created. To go ahead, add --confirm:";
547
+ echo " bash cmd-tools-ai-items.sh ${name} --propose --summary \"…\" --confirm";
548
+ return 0 ;
549
+ fi
550
+ if [ -z "${summary}" ] ; then
551
+ echo "";
552
+ echo " Say what it is for first: --summary \"…\" — it goes into the issue and the PR." ;
553
+ return 1 ;
554
+ fi
555
+ if ! command -v claude > /dev/null 2>&1 ; then
556
+ echo "" ;
557
+ echo " Claude Code CLI not found — cannot hand this over." ;
558
+ echo " install: https://claude.com/claude-code" ;
559
+ return 1 ;
560
+ fi
561
+
562
+ echo "";
563
+ echo " Handing it to the AI agent…";
564
+ local prompt;
565
+ prompt="Publish one local ${kind%s} to the ${AI_ITEMS_REPO} repository, following the Webship issue/PR rules and that repository's own RULES.md.
566
+
567
+ Source: ${path}$([ "${kind}" == 'ai-skills' ] && echo ' (the whole skill directory)')
568
+ Belongs at: ${item_repo_path} in the repository
569
+ Status: it is ${state} ${AI_ITEMS_REPO}@${AI_ITEMS_REF}
570
+ What it is for: ${summary}
571
+
572
+ Do this:
573
+ 1. Read the source, and the repository's README/RULES so the item matches how the shared ${kind} are written.
574
+ 2. File one issue on ${AI_ITEMS_REPO} describing the ${kind%s} and why it is needed.
575
+ 3. Push a review branch (a fork when forking is enabled, a same-repo feature branch otherwise) and open ONE pull request against ${AI_ITEMS_REF} containing only this item, referencing the issue, ending with the Webship Checkpoints checklist.
576
+ 4. Leave 'Reviewed by a human' and 'Code review by maintainers' unticked. Never merge. Never push to ${AI_ITEMS_REF} directly.
577
+ 5. Report the issue URL and the pull request URL.
578
+
579
+ Only this one item. Do not include unrelated local changes.";
580
+
581
+ claude -p "${prompt}" --allowedTools Read Glob Grep Bash --disallowedTools Write Edit --no-session-persistence ;
582
+ }
583
+
584
+ # Copy this machine's installed items into the workspace folder.
585
+ #
586
+ # A machine's ~/.claude also holds client and third-party items, and a
587
+ # workspace folder can end up in a shared repository — so this direction
588
+ # applies a name filter (default "webship"; "_all_" opts out) rather than
589
+ # sweeping everything across.
590
+ function ai_items_from_claude() {
591
+ local kind="$1" name="$2" filter="${3:-webship}";
592
+ local src target synced=0;
593
+ src=$(ai_items_claude_path "${kind}") || return 1 ;
594
+ target="${WORKSPACE_ROOT}/${kind}";
595
+
596
+ if [ ! -d "${src}" ]; then
597
+ echo "No ${src} on this machine — nothing to sync." ;
598
+ return 1 ;
599
+ fi
600
+ mkdir -p "${target}" ;
601
+
602
+ if [ "${kind}" == 'ai-skills' ] ; then
603
+ for d in "${src}"/*/ ; do
604
+ [ -f "${d}/SKILL.md" ] || continue ;
605
+ local base; base=$(basename "${d}") ;
606
+ if [ ! "${name}" == '_all_' ] && [ -n "${name}" ] && [ ! "${base}" == "${name}" ] ; then continue ; fi
607
+ if [ ! "${filter}" == '_all_' ] && [[ "${base}" != ${filter}* ]] ; then continue ; fi
608
+ rsync -a "${d}" "${target}/${base}/" && synced=$((synced+1)) ;
609
+ done
610
+ else
611
+ for f in "${src}"/*.md ; do
612
+ [ -f "${f}" ] || continue ;
613
+ local base; base=$(basename "${f}" .md) ;
614
+ case "${base}" in README|AGENTS|CLAUDE) continue ;; esac
615
+ if [ ! "${name}" == '_all_' ] && [ -n "${name}" ] && [ ! "${base}" == "${name}" ] ; then continue ; fi
616
+ if [ ! "${filter}" == '_all_' ] && [[ "${base}" != ${filter}* ]] ; then continue ; fi
617
+ cp "${f}" "${target}/" && synced=$((synced+1)) ;
618
+ done
619
+ fi
620
+ echo "Synced ${synced} ${kind} from ${src} (filter: ${filter}) into ${target}.";
621
+ }