@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,676 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Structured-Prompt-Driven Development (martinfowler.com/articles/structured-prompt-driven) for
4
+ # this workspace: the prompt for a change is an artifact that is written, reviewed, versioned and
5
+ # kept in sync with the code — not something typed into a chat and lost.
6
+ #
7
+ # One spec is a directory:
8
+ #
9
+ # specs/<workspace>/<project>/NNN-topic/
10
+ # 01-story.md the ask: problem, definition of done, acceptance criteria
11
+ # 02-analysis.md what already exists, what it touches, what could go wrong
12
+ # 03-canvas.md the REASONS Canvas — the contract the change is generated from
13
+ # 04-tests.md what proves it works
14
+ # spec.yml stage, links, and the commit the canvas was last true for
15
+ #
16
+ # The stages are gates, not decoration: analysis needs a story, a canvas needs an analysis. Each
17
+ # one is a file a person reads and edits before the next one is asked for — which is the whole
18
+ # method. The AI writes drafts; the gate is a human deciding the draft is right.
19
+ #
20
+ # THE RULE, from the article and enforced by `-a drift`: when reality diverges, fix the prompt
21
+ # first, then the code. A canvas that no longer describes the tree is worse than no canvas — it is
22
+ # a document people trust.
23
+ #
24
+ # What makes this workspace a good host for the method: SPDD's weakest step is analysis ("scan the
25
+ # codebase"). Here a project may already have a graphify knowledge graph (structure) and a Milvus
26
+ # RAG index (its prose, templates and configuration), and `spdd_context` feeds both into the
27
+ # analysis rather than asking a model to guess.
28
+
29
+ SPDD_STAGES="story analysis canvas tests done";
30
+
31
+ # Where the specs live. Outside every project, like the graphs store, and for the same reason:
32
+ # a repository we ship must not gain a spec directory because somebody planned a change to it.
33
+ function spdd_root() {
34
+ printf '%s/specs' "${WORKSPACE_ROOT}";
35
+ }
36
+
37
+ # specs/<workspace>/<project> — the target a spec is about. A spec for the tooling itself uses the
38
+ # `workspace` pseudo-project, which is the one case where the target is not a built site.
39
+ function spdd_target_dir() {
40
+ printf '%s/%s' "$(spdd_root)" "$1";
41
+ }
42
+
43
+ # A slug that is safe as a directory name and still readable a month later.
44
+ function spdd_slug() {
45
+ printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | sed -e 's/[^a-z0-9]\+/-/g' -e 's/^-//' -e 's/-$//' | cut -c1-48 ;
46
+ }
47
+
48
+ # The next spec number for a target, zero-padded. Per target rather than global: the numbers are
49
+ # for reading, and "the third change to website_starter" is more useful than "the 47th spec".
50
+ function spdd_next_number() {
51
+ local dir="$1" last;
52
+ last="$(ls -1 "${dir}" 2>/dev/null | sed -n 's/^\([0-9]\{3\}\)-.*$/\1/p' | sort -n | tail -1)";
53
+ printf '%03d' "$(( 10#${last:-0} + 1 ))";
54
+ }
55
+
56
+ # Resolve a spec by number, slug, or <workspace>/<project>/NNN-slug. Ambiguity is refused rather
57
+ # than guessed: the next command writes into whatever this returns.
58
+ function spdd_resolve() {
59
+ local wanted="$1" base matches;
60
+ base="$(spdd_root)";
61
+ [ -d "${base}" ] || return 0 ;
62
+ case "${wanted}" in
63
+ *..*) return 0 ;;
64
+ */*/*) [ -d "${base}/${wanted}" ] && printf '%s' "${base}/${wanted}" ; return 0 ;;
65
+ esac
66
+ matches="$(find "${base}" -mindepth 3 -maxdepth 3 -type d \( -name "${wanted}" -o -name "${wanted}-*" -o -name "*-${wanted}" \) 2>/dev/null | sort)";
67
+ printf '%s' "${matches}";
68
+ }
69
+
70
+ function spdd_stage_of() {
71
+ local spec="$1";
72
+ sed -n 's/^stage:[[:space:]]*\([a-z]*\).*/\1/p' "${spec}/spec.yml" 2>/dev/null | head -1 ;
73
+ }
74
+
75
+ function spdd_field() {
76
+ sed -n "s/^$2:[[:space:]]*//p" "$1/spec.yml" 2>/dev/null | head -1 ;
77
+ }
78
+
79
+ function spdd_set_field() {
80
+ local spec="$1" field="$2" value="$3" tmp;
81
+ tmp="$(mktemp)";
82
+ if grep -q "^${field}:" "${spec}/spec.yml" 2>/dev/null ; then
83
+ sed "s|^${field}:.*|${field}: ${value}|" "${spec}/spec.yml" > "${tmp}" && mv "${tmp}" "${spec}/spec.yml" ;
84
+ else
85
+ cp "${spec}/spec.yml" "${tmp}" 2>/dev/null ; printf '%s: %s\n' "${field}" "${value}" >> "${tmp}" ; mv "${tmp}" "${spec}/spec.yml" ;
86
+ fi
87
+ }
88
+
89
+ # The governance block every generated artifact is written against, straight out of
90
+ # core/config/norms.yml so a rule is stated once.
91
+ function spdd_norms_block() {
92
+ local file="${WORKSPACE_CONFIG}/norms.yml";
93
+ if [ ! -f "${file}" ] ; then
94
+ echo "(core/config/norms.yml is missing — the change has no stated norms or safeguards.)";
95
+ return 0 ;
96
+ fi
97
+ cat "${file}";
98
+ }
99
+
100
+ # What the model is allowed to know about the target before it writes anything.
101
+ #
102
+ # Deterministic sources first, and clearly labelled: the graph is structure, the index is prose,
103
+ # and the tree listing is what actually exists. A model told "here is the structure" invents less
104
+ # than one told "figure out the structure".
105
+ function spdd_context() {
106
+ local target="$1" workspace project project_path graph;
107
+ workspace="${target%%/*}";
108
+ project="${target##*/}";
109
+ project_path="${WORKSPACE_ROOT}/${workspace}/${project}";
110
+
111
+ echo "## The target";
112
+ echo "";
113
+ echo "${target} (${project_path})";
114
+ echo "";
115
+
116
+ if [ -d "${project_path}" ] ; then
117
+ echo "### Top-level layout";
118
+ echo '```';
119
+ ls -1 "${project_path}" 2>/dev/null | head -40 ;
120
+ echo '```';
121
+ echo "";
122
+ fi
123
+
124
+ graph="${WORKSPACE_ROOT}/graphs/${workspace}/${project}/graph.json";
125
+ if [ -f "${graph}" ] ; then
126
+ echo "### Structure (graphify knowledge graph)";
127
+ echo "";
128
+ echo "A graph exists at ${graph}. It maps PHP classes under src/, JS and SQL — NOT hooks in";
129
+ echo "*.module/*.install, NOT Twig, NOT any *.yml. Query it with:";
130
+ echo '```';
131
+ echo "graphify query \"...\" --graph ${graph}";
132
+ echo '```';
133
+ echo "";
134
+ fi
135
+
136
+ # The RAG index covers exactly what the graph does not: prose, templates and configuration.
137
+ # It is an enrichment, not a dependency: this workspace has no rag workspace yet, so the block
138
+ # is skipped unless those functions are loaded. Checked explicitly rather than relying on the
139
+ # errors being swallowed, so a missing index is a skipped section and not a hidden failure.
140
+ if declare -f rag_resolve_instance > /dev/null 2>&1 \
141
+ && command -v docker > /dev/null 2>&1 && [ -n "${SPDD_QUERY}" ] ; then
142
+ local instance uri collection hits;
143
+ instance="$(rag_resolve_instance "${workspace}" "${project}" '_settings_' 2>/dev/null)";
144
+ if [ -n "${instance}" ] && docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "ddev-${instance}-milvus-standalone" ; then
145
+ collection="$(rag_collection_name "${workspace}" "${project}")";
146
+ local doc_name_saved="${doc_name}"; doc_name="rag";
147
+ uri="$(milvus_uri "${instance}")";
148
+ doc_name="${doc_name_saved}";
149
+ hits="$(rag_python --uri "${uri}" --token "$(milvus_token)" search --collection "${collection}" \
150
+ --query "${SPDD_QUERY}" --limit 6 --context 14 2>/dev/null)";
151
+ if [ -n "${hits}" ] ; then
152
+ echo "### What the tree already says about this (RAG index, BM25)";
153
+ echo '```';
154
+ printf '%s\n' "${hits}";
155
+ echo '```';
156
+ echo "";
157
+ fi
158
+ fi
159
+ fi
160
+ }
161
+
162
+ # Ask the Claude Code CLI for a draft, with the prompt on STDIN.
163
+ #
164
+ # Printed rather than run when the CLI is missing: a draft nobody can reproduce is not an
165
+ # artifact, and the point of the method is that the prompt is the thing you keep.
166
+ function spdd_draft() {
167
+ local outfile="$1" title="$2";
168
+ if ! command -v claude > /dev/null 2>&1 ; then
169
+ echo "The claude CLI is not on PATH — writing the prompt to ${outfile}.prompt instead.";
170
+ cat > "${outfile}.prompt";
171
+ echo "Run it yourself with: claude -p \"\$(cat ${outfile}.prompt)\" > ${outfile}";
172
+ return 1 ;
173
+ fi
174
+ echo "Drafting ${title} — this calls the local Claude Code CLI and takes a minute.";
175
+ # Tool-free print mode: this step writes a document, and a step that could also edit the tree
176
+ # would blur which artifact the change came from.
177
+ claude -p --allowed-tools "" > "${outfile}" || return 1 ;
178
+ [ -s "${outfile}" ] || { echo "The draft came back empty — nothing written."; rm -f "${outfile}"; return 1 ; }
179
+ return 0 ;
180
+ }
181
+
182
+ # ---------------------------------------------------------------------------
183
+ # The stages. Each one refuses to run until the one before it exists, because a
184
+ # canvas written from an unread analysis is the failure the method exists to prevent.
185
+ # ---------------------------------------------------------------------------
186
+
187
+ # Stage 1 — the story. Written by hand from a sentence, not by a model: the ask is the one thing
188
+ # nobody should be generating.
189
+ function spdd_story() {
190
+ local target="${TARGET}" topic="${TOPIC}" dir slug number spec;
191
+ if [ "${topic}" == '_none_' ] || [ -z "${topic}" ] ; then
192
+ echo "Say what the change is: bash cmd-spdd.sh ${target} \"index a project into Milvus\"";
193
+ return 1 ;
194
+ fi
195
+ case "${target}" in
196
+ */*) : ;;
197
+ *) echo "The target is <workspace>/<project>, e.g. products/webship_core or dev/myproject." ; return 1 ;;
198
+ esac
199
+
200
+ dir="$(spdd_target_dir "${target}")";
201
+ mkdir -p "${dir}" ;
202
+ slug="$(spdd_slug "${topic}")";
203
+ number="$(spdd_next_number "${dir}")";
204
+ spec="${dir}/${number}-${slug}";
205
+
206
+ if [ -d "${spec}" ] ; then
207
+ echo "${spec} already exists.";
208
+ return 1 ;
209
+ fi
210
+ mkdir -p "${spec}" ;
211
+
212
+ cat > "${spec}/spec.yml" <<YMLEOF
213
+ # What this spec is about, and how far it has got. Written by cmd-spdd.sh; edit by hand freely.
214
+ target: ${target}
215
+ topic: ${topic}
216
+ stage: story
217
+ created: $(date +%Y-%m-%d)
218
+ # The commit of the target the canvas was last true for; -a drift compares against it.
219
+ # (No backticks in this heredoc: it interpolates \${target}, so a backtick would run as a command.)
220
+ synced_commit:
221
+ # Fill these in when they exist, so a merged change can be traced back to the prompt it came from.
222
+ issue:
223
+ pull_request:
224
+ YMLEOF
225
+
226
+ cat > "${spec}/01-story.md" <<STORYEOF
227
+ # ${topic}
228
+
229
+ ## Problem / motivation
230
+
231
+ <!-- What is wrong or missing today, for whom, and why it is worth doing. Write it as if the
232
+ reader has not been in the conversation this came from — in six months that reader is you. -->
233
+
234
+ ## Scope
235
+
236
+ **In:**
237
+ -
238
+
239
+ **Out:**
240
+ -
241
+
242
+ ## Definition of done
243
+
244
+ <!-- Observable outcomes, not tasks. "The dashboard row shows the bound server" — not
245
+ "add a badge". Each of these becomes an acceptance criterion the Canvas maps operations to. -->
246
+ - [ ]
247
+ - [ ]
248
+
249
+ ## Acceptance criteria
250
+
251
+ | # | Given | When | Then |
252
+ | - | ----- | ---- | ---- |
253
+ | 1 | | | |
254
+ STORYEOF
255
+
256
+ echo "";
257
+ echo "*---------------------------------------------------------------------*";
258
+ echo " Spec ${number}-${slug} started for ${target}.";
259
+ echo "";
260
+ echo " ${spec}/01-story.md";
261
+ echo "";
262
+ echo " Write the story yourself — the ask is the one artifact nobody should generate.";
263
+ echo " Then: bash cmd-spdd.sh ${target}/${number}-${slug} -a analysis";
264
+ echo "*---------------------------------------------------------------------*";
265
+ }
266
+
267
+ # Stage 2 — the analysis. What already exists, what this touches, what could go wrong. Seeded with
268
+ # the graph and the RAG index rather than with a model's memory of the tree.
269
+ function spdd_analysis() {
270
+ local spec="$1" target stage;
271
+ target="$(spdd_field "${spec}" target)";
272
+ stage="$(spdd_stage_of "${spec}")";
273
+ [ -s "${spec}/01-story.md" ] || { echo "No story yet in ${spec}." ; return 1 ; }
274
+ if grep -q '^## Problem / motivation$' "${spec}/01-story.md" && ! grep -qv '^\s*\(#\|<!--\|-\||\|\[\|$\)' "${spec}/01-story.md" ; then
275
+ echo "01-story.md is still the empty template — write the ask before asking for an analysis.";
276
+ return 1 ;
277
+ fi
278
+
279
+ SPDD_QUERY="$(spdd_field "${spec}" topic)";
280
+ {
281
+ echo "You are analysing a proposed change to the Webship Workspace, a DDEV-only Drupal";
282
+ echo "development harness. Produce ONLY a markdown analysis document — no preamble, no code.";
283
+ echo "";
284
+ echo "Write these sections, and nothing else:";
285
+ echo " ## What exists today — what is already there that this change touches or duplicates.";
286
+ echo " ## Domain vocabulary — the words this change is about, as this codebase uses them.";
287
+ echo " ## What it touches — files, commands and settings, as a list, with why each.";
288
+ echo " ## Risks — what could break, and what is easy to get subtly wrong.";
289
+ echo " ## Open questions — what a person must decide before a design is possible.";
290
+ echo "";
291
+ echo "State plainly when the provided context does not answer something. Do not invent a file";
292
+ echo "path, a function name or a command that is not shown to you.";
293
+ echo "";
294
+ echo "# The ask";
295
+ echo "";
296
+ cat "${spec}/01-story.md";
297
+ echo "";
298
+ spdd_context "${target}";
299
+ echo "# The rules this workspace holds to";
300
+ echo "";
301
+ spdd_norms_block;
302
+ } | spdd_draft "${spec}/02-analysis.md" "the analysis" || return 1 ;
303
+
304
+ spdd_set_field "${spec}" stage analysis ;
305
+ echo "";
306
+ echo " Read and correct ${spec}/02-analysis.md — it is the input to the contract.";
307
+ echo " Then: bash cmd-spdd.sh $(spdd_field "${spec}" target)/$(basename "${spec}") -a canvas";
308
+ }
309
+
310
+ # Stage 3 — the REASONS Canvas. The contract: requirements, entities, approach, structure,
311
+ # operations, norms, safeguards. Everything after this is generated FROM it.
312
+ function spdd_canvas() {
313
+ local spec="$1" target;
314
+ target="$(spdd_field "${spec}" target)";
315
+ [ -s "${spec}/02-analysis.md" ] || { echo "No analysis yet — run -a analysis first." ; return 1 ; }
316
+
317
+ SPDD_QUERY="$(spdd_field "${spec}" topic)";
318
+ {
319
+ echo "Write a REASONS Canvas for this change: the contract an implementation is generated from.";
320
+ echo "Output ONLY the markdown document, starting at '# REASONS Canvas —'.";
321
+ echo "";
322
+ echo "Sections, in this order, each a '## ' heading:";
323
+ echo " ## R — Requirements the why, the scope boundary, and the definition of done";
324
+ echo " ## E — Entities the things involved and how they relate (a mermaid classDiagram";
325
+ echo " when there is real structure; a table when there is not)";
326
+ echo " ## A — Approach the strategy chosen, and what was rejected and why";
327
+ echo " ## S — Structure which files, commands and settings, and how they fit what exists";
328
+ echo " ## O — Operations ordered, individually reviewable steps. Each names the file it";
329
+ echo " touches and states how it is verified. No step may be 'and so on'.";
330
+ echo " ## N — Norms the standards below, as they apply HERE — not copied verbatim";
331
+ echo " ## S — Safeguards the boundaries below, as they apply HERE, plus any specific to";
332
+ echo " this change (what must not be deleted, what must stay reversible)";
333
+ echo "";
334
+ echo "Rules for you: every operation must trace to a definition-of-done item. If the analysis";
335
+ echo "leaves a question open, say so in Requirements as an assumption rather than inventing an";
336
+ echo "answer. Do not write an operation the analysis gives you no evidence is possible.";
337
+ echo "";
338
+ echo "# The ask";
339
+ echo "";
340
+ cat "${spec}/01-story.md";
341
+ echo "";
342
+ echo "# The analysis (already reviewed by a person)";
343
+ echo "";
344
+ cat "${spec}/02-analysis.md";
345
+ echo "";
346
+ spdd_context "${target}";
347
+ echo "# The rules this workspace holds to";
348
+ echo "";
349
+ spdd_norms_block;
350
+ } | spdd_draft "${spec}/03-canvas.md" "the REASONS Canvas" || return 1 ;
351
+
352
+ spdd_set_field "${spec}" stage canvas ;
353
+ spdd_set_field "${spec}" synced_commit "$(spdd_head_of "${target}")" ;
354
+ echo "";
355
+ echo " Review ${spec}/03-canvas.md as a contract, not a draft: the operations are what gets built.";
356
+ echo " Then implement it — by hand, or by giving the canvas to a session:";
357
+ echo " claude \"Implement the operations in $(basename "${spec}")/03-canvas.md, one at a time.\"";
358
+ echo " And when the code and the canvas diverge: -a drift then -a sync";
359
+ }
360
+
361
+ # The commit a target is at, when it is a git repository. A built Drupal site is not one, and that
362
+ # is fine — drift detection simply has nothing to compare and says so.
363
+ function spdd_head_of() {
364
+ local target="$1" path;
365
+ path="${WORKSPACE_ROOT}/${target}";
366
+ git -C "${path}" rev-parse --short HEAD 2>/dev/null;
367
+ }
368
+
369
+ # Has the tree moved since the canvas was written? THE rule of the method is "fix the prompt
370
+ # first", and this is the only part of it a script can do: notice, and refuse to be quiet.
371
+ function spdd_drift() {
372
+ local spec="$1" target synced now;
373
+ target="$(spdd_field "${spec}" target)";
374
+ synced="$(spdd_field "${spec}" synced_commit)";
375
+ now="$(spdd_head_of "${target}")";
376
+
377
+ echo "";
378
+ echo " spec: $(basename "${spec}")";
379
+ echo " target: ${target}";
380
+ if [ -z "${now}" ] ; then
381
+ echo " status: the target is not a git repository — drift cannot be measured here.";
382
+ echo " Re-read the canvas against the tree yourself before trusting it.";
383
+ return 0 ;
384
+ fi
385
+ if [ -z "${synced}" ] ; then
386
+ echo " status: no synced commit recorded. Set it once the canvas matches the tree:";
387
+ echo " bash cmd-spdd.sh $(basename "${spec}") -a sync";
388
+ return 0 ;
389
+ fi
390
+ if [ "${synced}" == "${now}" ] ; then
391
+ echo " status: in sync at ${now}.";
392
+ return 0 ;
393
+ fi
394
+ echo " status: DRIFTED — canvas is true for ${synced}, the target is at ${now}.";
395
+ echo "";
396
+ git -C "${WORKSPACE_ROOT}/${target}" log --oneline "${synced}..${now}" 2>/dev/null | head -10 ;
397
+ echo "";
398
+ echo " Fix the PROMPT first, then the code:";
399
+ echo " 1. edit 03-canvas.md so it describes what is now true";
400
+ echo " 2. bash cmd-spdd.sh $(basename "${spec}") -a sync";
401
+ return 1 ;
402
+ }
403
+
404
+ # Record that the canvas has been made true again. Deliberately a separate, explicit act: a script
405
+ # that stamped this automatically would be recording an opinion nobody formed.
406
+ function spdd_sync() {
407
+ local spec="$1" target now;
408
+ target="$(spdd_field "${spec}" target)";
409
+ now="$(spdd_head_of "${target}")";
410
+ if [ -z "${now}" ] ; then
411
+ echo "The target is not a git repository — nothing to stamp.";
412
+ return 1 ;
413
+ fi
414
+ spdd_set_field "${spec}" synced_commit "${now}" ;
415
+ echo "$(basename "${spec}") is recorded as true for ${target} at ${now}.";
416
+ }
417
+
418
+ function spdd_list() {
419
+ local base spec target stage topic;
420
+ base="$(spdd_root)";
421
+ if [ -z "$(find "${base}" -mindepth 3 -maxdepth 3 -type d 2>/dev/null)" ] ; then
422
+ echo "No specs yet. Start one from its target's workspace:";
423
+ echo " cd ${WORKSPACE_ROOT}/specs && bash cmd-spdd.sh <workspace>/<project> \"what the change is\"";
424
+ return 0 ;
425
+ fi
426
+ printf '%-34s %-26s %-9s %s\n' "SPEC" "TARGET" "STAGE" "TOPIC";
427
+ while read -r spec ; do
428
+ [ -n "${spec}" ] || continue ;
429
+ target="$(spdd_field "${spec}" target)";
430
+ stage="$(spdd_stage_of "${spec}")";
431
+ topic="$(spdd_field "${spec}" topic)";
432
+ printf '%-34s %-26s %-9s %s\n' "$(basename "${spec}")" "${target}" "${stage:-?}" "${topic}";
433
+ done <<< "$(find "${base}" -mindepth 3 -maxdepth 3 -type d 2>/dev/null | sort)"
434
+ }
435
+
436
+ # One spec, or a sentence saying why not. Sets SPDD_SPEC.
437
+ function spdd_require_spec() {
438
+ local matches count;
439
+ if [ "${SPEC_NAME}" == '_all_' ] || [ -z "${SPEC_NAME}" ] ; then
440
+ echo "Name the spec: bash cmd-spdd.sh <spec> -a ${SPDD_ACTION} ( -a list shows them )";
441
+ return 1 ;
442
+ fi
443
+ matches="$(spdd_resolve "${SPEC_NAME}")";
444
+ count="$(printf '%s' "${matches}" | grep -c . )";
445
+ if [ "${count}" -eq 0 ] ; then echo "No spec matches '${SPEC_NAME}'." ; return 1 ; fi
446
+ if [ "${count}" -gt 1 ] ; then
447
+ echo "'${SPEC_NAME}' matches ${count} specs — name the target too:";
448
+ while read -r m ; do printf ' %s\n' "${m#$(spdd_root)/}" ; done <<< "${matches}"
449
+ return 1 ;
450
+ fi
451
+ SPDD_SPEC="${matches}";
452
+ return 0 ;
453
+ }
454
+
455
+
456
+ # ---------------------------------------------------------------------------
457
+ # Writing, reading, reviewing — the three things the published method leaves to
458
+ # somebody's discipline, and the three that decide whether any of it is kept up.
459
+ # ---------------------------------------------------------------------------
460
+
461
+ # Where the shared templates live. A template that is a FILE can be reviewed, improved and shared;
462
+ # one that is a heredoc in this script can only be edited by whoever is willing to edit this script.
463
+ function spdd_default_template() {
464
+ local name="$1" file;
465
+ file="${WORKSPACE_ROOT}/specs/_defaults/${name}";
466
+ [ -f "${file}" ] && echo "${file}" ;
467
+ }
468
+
469
+ # The general spec of a product: what it is, who for, what it must never become.
470
+ #
471
+ # SPDD as published has no such layer — it goes straight to a story — and the tool comparison
472
+ # (martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html) names the gap: every tool separates
473
+ # a "memory bank" of durable project context from the task-specific spec, and the ones that do not
474
+ # make people restate the context in every spec. Spec Kit calls it a constitution; so does this.
475
+ #
476
+ # It is a file per product, written by a person, revised rarely. This verb creates it from the
477
+ # template and otherwise gets out of the way — the one artifact nobody should generate is what the
478
+ # product IS.
479
+ function spdd_constitution() {
480
+ local target="${SPEC_NAME}" dir file template;
481
+ case "${target}" in
482
+ */*) : ;;
483
+ *) echo "Which product? bash cmd-spdd.sh products/webship -a constitution" ; return 1 ;;
484
+ esac
485
+ dir="$(spdd_target_dir "${target}")";
486
+ file="${dir}/00-constitution.md";
487
+
488
+ if [ -f "${file}" ] ; then
489
+ echo "${file}";
490
+ echo "";
491
+ sed -n '1,40p' "${file}";
492
+ echo "";
493
+ echo " (read it in full before writing any spec against ${target})";
494
+ return 0 ;
495
+ fi
496
+
497
+ template="$(spdd_default_template 00-constitution.md)";
498
+ if [ -z "${template}" ] ; then
499
+ echo "No template at specs/_defaults/00-constitution.md — pull the shared specs first:";
500
+ echo " cd ${WORKSPACE_ROOT}/specs && bash cmd-tools-specs.sh --pull";
501
+ return 1 ;
502
+ fi
503
+ mkdir -p "${dir}" ;
504
+ sed "s|<Product>|${target##*/}|g" "${template}" > "${file}" ;
505
+ echo "";
506
+ echo "*---------------------------------------------------------------------*";
507
+ echo " Constitution started for ${target}.";
508
+ echo "";
509
+ echo " ${file}";
510
+ echo "";
511
+ echo " Write it yourself. A constitution is the one document that outlives every";
512
+ echo " change to the product, and a generated one governs nothing.";
513
+ echo "*---------------------------------------------------------------------*";
514
+ }
515
+
516
+ # Everything a session needs to implement this spec, as ONE document, in reading order.
517
+ #
518
+ # This is the "using" half, and it is the half that decides whether the artifacts were worth
519
+ # writing. A spec spread over four files is read by nobody at the moment it matters; `-a read`
520
+ # prints the constitution, the story, the analysis and the canvas in order, so the thing handed to
521
+ # a session — or to a person on their first day — is complete and in one piece.
522
+ function spdd_read() {
523
+ local spec="$1" target constitution f;
524
+ target="$(spdd_field "${spec}" target)";
525
+ echo "# ${target} — $(spdd_field "${spec}" topic)";
526
+ echo "";
527
+ echo "_Stage: $(spdd_stage_of "${spec}"). Read in this order: what the product is, then the ask,";
528
+ echo "then what the tree says, then the contract._";
529
+ echo "";
530
+
531
+ constitution="$(spdd_target_dir "${target}")/00-constitution.md";
532
+ if [ -f "${constitution}" ] ; then
533
+ echo "---";
534
+ echo "";
535
+ cat "${constitution}" ;
536
+ echo "";
537
+ else
538
+ echo "> **No constitution for ${target}.** Nothing states what this product is, who it is for or";
539
+ echo "> what it must never become, so nothing can say whether this change belongs in it.";
540
+ echo "> Start one: bash cmd-spdd.sh ${target} -a constitution";
541
+ echo "";
542
+ fi
543
+
544
+ for f in 01-story.md 02-analysis.md 03-canvas.md ; do
545
+ if [ -f "${spec}/${f}" ] ; then
546
+ echo "---";
547
+ echo "";
548
+ cat "${spec}/${f}" ;
549
+ echo "";
550
+ fi
551
+ done
552
+ }
553
+
554
+ # The mechanical half of specs/_defaults/REVIEW.md.
555
+ #
556
+ # It never says "approved". A checklist can see that a section is missing, not that it is right —
557
+ # and the tool comparison's sharpest complaint is the review burden ("I'd rather review code than
558
+ # all these markdown files"), which is made worse, not better, by a tool that pretends to have done
559
+ # the reading. What this does is the part a person should not be spending attention on: is the
560
+ # scope boundary there, does every definition-of-done item have an operation, are all seven canvas
561
+ # sections present. What it cannot judge, it lists as what is left for the reviewer.
562
+ function spdd_review() {
563
+ local spec="$1" target stage story canvas problems=0 notes=0 line;
564
+ target="$(spdd_field "${spec}" target)";
565
+ stage="$(spdd_stage_of "${spec}")";
566
+ story="${spec}/01-story.md";
567
+ canvas="${spec}/03-canvas.md";
568
+
569
+ echo "";
570
+ echo "Review — $(spdd_field "${spec}" topic)";
571
+ echo " ${spec}";
572
+ echo " target ${target}, stage ${stage}";
573
+ echo "";
574
+
575
+ # Gate 0 — the constitution.
576
+ if [ -f "$(spdd_target_dir "${target}")/00-constitution.md" ] ; then
577
+ echo " ok ${target} has a constitution to check this against";
578
+ else
579
+ echo " NOTE ${target} has no 00-constitution.md — nothing says whether this change belongs";
580
+ notes=$((notes + 1));
581
+ fi
582
+
583
+ # Gate 1 — the story.
584
+ if [ -f "${story}" ] ; then
585
+ grep -q '^\*\*Out:\*\*' "${story}" && echo " ok the story has an Out list" || {
586
+ echo " MISS the story has no **Out:** list — an In list alone constrains nothing";
587
+ problems=$((problems + 1)); }
588
+ if grep -qE '^- \[[ x]\]' "${story}" ; then
589
+ echo " ok the story has a definition of done";
590
+ else
591
+ echo " MISS the story has no definition of done"; problems=$((problems + 1));
592
+ fi
593
+ grep -q 'Given | When | Then' "${story}" && echo " ok the story has acceptance criteria" || {
594
+ echo " MISS the story has no Given/When/Then acceptance criteria"; problems=$((problems + 1)); }
595
+ else
596
+ echo " MISS no 01-story.md"; problems=$((problems + 1));
597
+ fi
598
+
599
+ # Gate 3 — the canvas: all seven sections, and every D-item served.
600
+ if [ -f "${canvas}" ] ; then
601
+ local missing='';
602
+ for sec in 'R — Requirements' 'E — Entities' 'A — Approach' 'S — Structure' \
603
+ 'O — Operations' 'N — Norms' 'S — Safeguards' ; do
604
+ grep -q "^## ${sec}" "${canvas}" || missing="${missing} ${sec%% *}";
605
+ done
606
+ if [ -z "${missing}" ] ; then
607
+ echo " ok the canvas has all seven REASONS sections";
608
+ else
609
+ echo " MISS the canvas is missing section(s):${missing}"; problems=$((problems + 1));
610
+ fi
611
+
612
+ # Every D-item named in the canvas should be served by at least one operation row.
613
+ local d unserved='';
614
+ for d in $(grep -oE '^\| D[0-9]+' "${canvas}" | tr -d '| ' | sort -u) ; do
615
+ grep -q "${d}" <(sed -n '/^## O — Operations/,/^## N —/p' "${canvas}") || unserved="${unserved} ${d}";
616
+ done
617
+ if [ -n "${unserved}" ] ; then
618
+ echo " MISS no operation serves:${unserved} — out of scope, or forgotten? Decide in writing";
619
+ problems=$((problems + 1));
620
+ elif grep -q '^## O — Operations' "${canvas}" ; then
621
+ echo " ok every definition-of-done item is served by an operation";
622
+ fi
623
+ elif [ "${stage}" == 'canvas' ] ; then
624
+ echo " MISS stage says canvas, but there is no 03-canvas.md"; problems=$((problems + 1));
625
+ fi
626
+
627
+ # Has the tree moved under it? The drift check is part of the review, not a separate errand.
628
+ local synced head;
629
+ synced="$(spdd_field "${spec}" synced_commit)";
630
+ if [ -n "${synced}" ] ; then
631
+ head="$(spdd_head_of "${target}")";
632
+ if [ -n "${head}" ] && [ "${head}" != "${synced}" ] ; then
633
+ echo " NOTE the target has moved since the canvas was stamped (${synced} → ${head}) — -a drift";
634
+ notes=$((notes + 1));
635
+ fi
636
+ fi
637
+
638
+ echo "";
639
+ echo " What this cannot judge, and a reviewer must:";
640
+ echo " · is the problem stated as a problem, or already as its solution?";
641
+ echo " · does the analysis CONTRADICT the story anywhere? one that agrees with everything";
642
+ echo " was not an analysis";
643
+ echo " · does the approach name the alternatives it rejected, and why?";
644
+ echo " · is the ask inside the constitution's In-scope column?";
645
+ echo "";
646
+ if [ "${problems}" -gt 0 ] ; then
647
+ echo " ${problems} thing(s) to fix before this is ready to be read by somebody else.";
648
+ else
649
+ echo " Nothing mechanical is missing. The reading above is still yours to do.";
650
+ fi
651
+ echo " The full checklist: ${WORKSPACE_ROOT}/specs/_defaults/REVIEW.md";
652
+ echo "";
653
+ return 0 ;
654
+ }
655
+
656
+ function spdd_command() {
657
+ case "${SPDD_ACTION}" in
658
+ list) spdd_list ;;
659
+ story) TARGET="${SPEC_NAME}" ; spdd_story ;;
660
+ # Takes a TARGET rather than a spec: a constitution is about the product, not about a change.
661
+ constitution) spdd_constitution ;;
662
+ analysis|canvas|drift|sync|show|read|review)
663
+ spdd_require_spec || return 1 ;
664
+ case "${SPDD_ACTION}" in
665
+ analysis) spdd_analysis "${SPDD_SPEC}" ;;
666
+ canvas) spdd_canvas "${SPDD_SPEC}" ;;
667
+ drift) spdd_drift "${SPDD_SPEC}" ;;
668
+ sync) spdd_sync "${SPDD_SPEC}" ;;
669
+ read) spdd_read "${SPDD_SPEC}" ;;
670
+ review) spdd_review "${SPDD_SPEC}" ;;
671
+ show) echo "${SPDD_SPEC}" ; ls -1 "${SPDD_SPEC}" ;;
672
+ esac
673
+ ;;
674
+ *) echo "Unknown action '${SPDD_ACTION}'." ; return 1 ;;
675
+ esac
676
+ }