jackherd 1.9.0 → 1.10.0

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 (204) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/INSTALL.md +1 -1
  3. package/README.md +50 -32
  4. package/dist/{chunk-MCP4XJQH.js → chunk-5MQBCUQ5.js} +9 -4
  5. package/dist/config.d.ts +39 -4
  6. package/dist/config.js +67 -26
  7. package/dist/plan-format.d.ts +45 -6
  8. package/dist/plan-format.js +46 -6
  9. package/docs/workflow-dark.svg +67 -63
  10. package/docs/workflow.svg +67 -63
  11. package/herdr-plugin/herdr-plugin.toml +1 -1
  12. package/install.sh +1 -1
  13. package/kit/hooks/ask-rules.json +15 -0
  14. package/kit/hooks/hooks/ask-hint/decision.ts +82 -14
  15. package/kit/hooks/hooks/ask-hint/index.ts +15 -19
  16. package/kit/hooks/hooks/ask-hint/settings.ts +3 -18
  17. package/kit/hooks/hooks/git-guard/decision.ts +50 -102
  18. package/kit/hooks/hooks/merge-guard/always-ask.ts +63 -0
  19. package/kit/hooks/hooks/merge-guard/{plan-approval-settings.ts → board-settings.ts} +21 -67
  20. package/kit/hooks/hooks/merge-guard/check-free.ts +20 -0
  21. package/kit/hooks/hooks/merge-guard/decision.ts +127 -187
  22. package/kit/hooks/hooks/merge-guard/index.ts +6 -3
  23. package/kit/hooks/hooks/merge-guard/lane-plan.ts +79 -0
  24. package/kit/hooks/hooks/merge-guard/run.ts +59 -54
  25. package/kit/hooks/hooks/merge-guard/settings.ts +13 -1
  26. package/kit/hooks/hooks/merge-guard/standing-approval.ts +142 -0
  27. package/kit/hooks/hooks/open-question/index.ts +26 -0
  28. package/kit/hooks/hooks/open-question/record.ts +135 -0
  29. package/kit/hooks/hooks/open-question/run.ts +52 -0
  30. package/kit/hooks/hooks/open-question/store.ts +39 -0
  31. package/kit/hooks/hooks/sent-file/store.ts +3 -12
  32. package/kit/hooks/hooks/shared/config.ts +7 -0
  33. package/kit/hooks/hooks/shared/dialog-answers.ts +9 -9
  34. package/kit/hooks/hooks/shared/files.ts +7 -0
  35. package/kit/hooks/hooks/shared/lane.ts +1 -1
  36. package/kit/hooks/hooks/shared/newest-verdict.ts +10 -0
  37. package/kit/hooks/hooks/shared/private-directory.ts +56 -9
  38. package/kit/hooks/hooks/shared/program-position.ts +2 -75
  39. package/kit/hooks/hooks/shared/shell-aliases.ts +1 -71
  40. package/kit/hooks/hooks/{ask-hint → shared}/transcript.ts +37 -6
  41. package/kit/hooks/hooks/{merge-guard → shared}/xargs-program.ts +10 -8
  42. package/kit/hooks/hooks/task-hint/decision.ts +49 -0
  43. package/kit/hooks/hooks/task-hint/index.ts +79 -0
  44. package/kit/hooks/hooks/task-hint/run.ts +62 -0
  45. package/kit/hooks/hooks/task-hint/store.ts +51 -0
  46. package/kit/hooks/hooks.json +13 -5
  47. package/kit/review/agents/skill-review.md +34 -0
  48. package/kit/review/skills/pre-commit/SKILL.md +1 -1
  49. package/kit/workflow/skills/follow/SKILL.md +1 -8
  50. package/kit/workflow/skills/implement/SKILL.md +8 -6
  51. package/kit/workflow/skills/orchestrate/SKILL.md +210 -433
  52. package/kit/workflow/skills/orchestrate/handover/decision.ts +43 -8
  53. package/kit/workflow/skills/orchestrate/handover/index.ts +32 -4
  54. package/kit/workflow/skills/orchestrate/handover/standing-approval.ts +84 -0
  55. package/kit/workflow/skills/orchestrate/references/acceptance.md +15 -0
  56. package/kit/workflow/skills/orchestrate/references/close-lane.md +17 -0
  57. package/kit/workflow/skills/orchestrate/references/coordinator-handover.md +43 -17
  58. package/kit/workflow/skills/orchestrate/references/draft-review.md +19 -0
  59. package/kit/workflow/skills/orchestrate/references/events.md +86 -0
  60. package/kit/workflow/skills/orchestrate/references/herd.md +6 -6
  61. package/kit/workflow/skills/orchestrate/references/lane-handover.md +18 -0
  62. package/kit/workflow/skills/orchestrate/references/lane-session.md +25 -0
  63. package/kit/workflow/skills/orchestrate/references/lane-setup.md +37 -0
  64. package/kit/workflow/skills/orchestrate/references/plan-commit.md +22 -10
  65. package/kit/workflow/skills/orchestrate/references/sensitive-input.md +5 -7
  66. package/kit/workflow/skills/plan/SKILL.md +6 -5
  67. package/kit/workflow/skills/plan/references/template-English.md +4 -4
  68. package/kit/workflow/skills/plan/references/template-German.md +4 -4
  69. package/package.json +3 -1
  70. package/recipes/README.md +2 -1
  71. package/recipes/sources.md +1 -1
  72. package/schemas/jackherd.schema.json +59 -5
  73. package/src/check/agent-text-reviewers.ts +23 -0
  74. package/src/check/command.ts +2 -0
  75. package/src/check/plan-structure-check.ts +49 -2
  76. package/src/check/plan.ts +8 -1
  77. package/src/check/schema.ts +9 -0
  78. package/src/check/skill-budget-defaults.ts +55 -0
  79. package/src/check/skill-budgets.ts +185 -0
  80. package/src/cli.ts +1 -0
  81. package/src/config/auto-window.ts +8 -0
  82. package/src/config/link-kinds.ts +7 -1
  83. package/src/config/schema.ts +58 -24
  84. package/src/lane/close-board.ts +106 -0
  85. package/src/lane/close-check.ts +183 -0
  86. package/src/lane/close-command-line.ts +11 -0
  87. package/src/lane/close-command.ts +217 -0
  88. package/src/lane/close-options.ts +43 -0
  89. package/src/lane/close-survey.ts +83 -0
  90. package/src/lane/close.ts +141 -0
  91. package/src/lane/command.ts +13 -0
  92. package/src/lane-prompt/build.ts +74 -0
  93. package/src/lane-prompt/command.ts +87 -0
  94. package/src/lane-prompt/input.ts +116 -0
  95. package/src/lane-prompt/options.ts +68 -0
  96. package/src/lane-prompt/texts.ts +101 -0
  97. package/src/plan-format/clock.ts +8 -0
  98. package/src/plan-format/dependencies.ts +7 -4
  99. package/src/plan-format/index.ts +1 -0
  100. package/src/plan-format/short-names.ts +71 -0
  101. package/src/plan-format/waiting.ts +7 -2
  102. package/src/plans/appointment-rows.ts +59 -0
  103. package/src/plans/ask-coordinator.ts +77 -0
  104. package/src/plans/ask-target.ts +25 -0
  105. package/src/plans/child-stop.ts +51 -0
  106. package/src/plans/closable-records.ts +34 -0
  107. package/src/plans/command.ts +3 -2
  108. package/src/plans/fetch.ts +26 -3
  109. package/src/plans/git.ts +19 -5
  110. package/src/plans/herd-claim.ts +173 -0
  111. package/src/plans/herd-memory.ts +36 -25
  112. package/src/plans/lane-plans.ts +44 -13
  113. package/src/plans/lane-summary.ts +253 -45
  114. package/src/plans/layout-phase.ts +29 -7
  115. package/src/plans/layout-summary.ts +89 -18
  116. package/src/plans/layout.ts +262 -68
  117. package/src/plans/link-chips.ts +1 -0
  118. package/src/plans/live.ts +107 -20
  119. package/src/plans/phase-ids.ts +8 -0
  120. package/src/plans/phase-source.ts +15 -0
  121. package/src/plans/plan-cache.ts +28 -0
  122. package/src/plans/plan-heads.ts +54 -0
  123. package/src/plans/pull-requests.ts +17 -2
  124. package/src/plans/question-records.ts +48 -0
  125. package/src/plans/questions-read.ts +13 -0
  126. package/src/plans/read-cache.ts +8 -0
  127. package/src/plans/record-id.ts +4 -0
  128. package/src/plans/restart-decision.ts +13 -4
  129. package/src/plans/selection.ts +98 -18
  130. package/src/plans/sent-files-read.ts +6 -15
  131. package/src/plans/sent-files.ts +7 -14
  132. package/src/plans/settings.ts +2 -1
  133. package/src/plans/signalled-phase.ts +42 -0
  134. package/src/plans/signals-read.ts +12 -4
  135. package/src/plans/snapshot.ts +85 -62
  136. package/src/plans/sprint-export.ts +11 -11
  137. package/src/plans/sprint-page.ts +5 -5
  138. package/src/plans/subagent-phases.ts +95 -0
  139. package/src/plans/subagent-tasks.ts +51 -0
  140. package/src/plans/subagents-read.ts +66 -17
  141. package/src/plans/subagents.ts +64 -6
  142. package/src/plans/supervisor.ts +14 -4
  143. package/src/plans/symbols.ts +3 -0
  144. package/src/plans/task-name.ts +34 -0
  145. package/src/plans/texts.ts +23 -6
  146. package/src/plans/view.ts +17 -9
  147. package/src/plans/waiting-click.ts +192 -37
  148. package/src/plans/waiting-records.ts +39 -0
  149. package/src/plans/waiting-window.ts +8 -0
  150. package/src/preview/command.ts +25 -3
  151. package/src/shared/private-location.ts +89 -0
  152. package/src/shared/stat-cache.ts +48 -0
  153. package/src/sync/kit-source.ts +29 -10
  154. package/src/sync/kit.ts +2 -0
  155. package/src/sync/manifest.ts +2 -0
  156. package/src/sync/plan-ask-rules.ts +94 -0
  157. package/src/sync/plan-hooks.ts +2 -0
  158. package/src/sync/render.ts +3 -3
  159. package/src/sync/settings-hooks.ts +4 -1
  160. package/src/sync/sync-plan.ts +1 -0
  161. package/src/sync/sync.ts +24 -3
  162. package/src/task-signals/claude-read.ts +27 -10
  163. package/src/terminal/text.ts +8 -3
  164. package/src/version/skill-diff.ts +46 -0
  165. package/src/vocabulary/index.ts +16 -6
  166. package/src/waiting/command.ts +22 -7
  167. package/src/watch/appointments.ts +1 -2
  168. package/src/watch/asking-lanes.ts +40 -0
  169. package/src/watch/base-tip.ts +11 -0
  170. package/src/watch/command.ts +330 -105
  171. package/src/watch/heartbeat-progress.ts +6 -18
  172. package/src/watch/kit-update.ts +28 -9
  173. package/src/watch/lane-name.ts +6 -0
  174. package/src/watch/lane-progress.ts +40 -8
  175. package/src/watch/lane-report-file.ts +20 -5
  176. package/src/watch/lane-report.ts +32 -4
  177. package/src/watch/lane-sources.ts +31 -3
  178. package/src/watch/main-checkout.ts +34 -0
  179. package/src/watch/plan-badges-read.ts +13 -7
  180. package/src/watch/pull-requests.ts +6 -2
  181. package/src/watch/reference-paths.ts +87 -0
  182. package/src/watch/running-subagents.ts +43 -0
  183. package/src/watch/skill-change.ts +124 -32
  184. package/src/watch/source-change.ts +48 -0
  185. package/src/watch/sources.ts +6 -3
  186. package/src/watch/stream-output.ts +4 -3
  187. package/src/watch/task-parallel-auto.ts +208 -0
  188. package/src/watch/task-parallel-data-paths.ts +46 -0
  189. package/src/watch/task-parallel-input.ts +5 -15
  190. package/src/watch/task-parallel-read.ts +3 -1
  191. package/src/watch/task-parallel.ts +36 -7
  192. package/src/watch/task-signal-missing.ts +69 -0
  193. package/src/watch/task-signal-read.ts +29 -0
  194. package/src/watch/texts.ts +10 -0
  195. package/src/watch/update-available.ts +6 -3
  196. package/src/watch/watch-events.ts +23 -0
  197. package/src/watch/watcher-lock.ts +25 -29
  198. package/kit/hooks/hooks/merge-guard/plan-approval-record.ts +0 -23
  199. package/kit/hooks/hooks/merge-guard/plan-approval-sources.ts +0 -196
  200. package/kit/hooks/hooks/merge-guard/plan-approval.ts +0 -168
  201. package/kit/hooks/hooks/merge-guard/plan-growth.ts +0 -40
  202. package/kit/hooks/hooks/merge-guard/plan-pointers.ts +0 -194
  203. package/kit/hooks/hooks/shared/command-text-runners.ts +0 -71
  204. package/kit/hooks/hooks/shared/plan-open-phases.ts +0 -114
package/docs/workflow.svg CHANGED
@@ -1,14 +1,14 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="960" height="724" viewBox="0 0 960 724" role="img" aria-labelledby="t d"><title id="t">jackherd in herdr</title><desc id="d">Animated schematic of one herdr window, told in five numbered steps. 1, Lane search works: its state is a teal dot and its progress counts up. 2, It hits an open question: search turns to an amber diamond and sends the question as text. 3, The coordinator asks you: the question appears in the coordinator pane as a choice. 4, You pick: option 1 is selected. 5, The lane works on: the answer goes back to search, which is a teal dot again, and the herd counts one more box.</desc><defs><style>.s{font-family:ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif}.m{font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace}@keyframes k1{0%{opacity:1.0}0%{opacity:1.0}38.12%{opacity:1.0}40%{opacity:0.42}80%{opacity:0.42}81.88%{opacity:1.0}100%{opacity:1.0}}.k1{animation:k1 16s linear infinite both}@keyframes k2{0%{opacity:0.42}40%{opacity:0.42}41.88%{opacity:1.0}81.88%{opacity:1.0}83.75%{opacity:0.42}100%{opacity:0.42}}.k2{animation:k2 16s linear infinite both}@keyframes k3{0%{opacity:0.42}100%{opacity:0.42}}.k3{animation:k3 16s linear infinite both}@keyframes k4{0%{opacity:0}21.25%{opacity:0}23.75%{opacity:1}82.5%{opacity:1}85%{opacity:0}100%{opacity:0}}.k4{animation:k4 16s linear infinite both}@keyframes k5{0%{opacity:0;transform:translate(64px,198px)}23.75%{opacity:0;transform:translate(64px,198px)}23.94%{opacity:1;transform:translate(64px,198px)}24.32%{opacity:1;transform:translate(60.4px,197.5px)}24.9%{opacity:1;transform:translate(57.1px,196.1px)}25.47%{opacity:1;transform:translate(54.2px,193.9px)}26.04%{opacity:1;transform:translate(51.6px,191.1px)}26.61%{opacity:1;transform:translate(49.4px,187.6px)}27.19%{opacity:1;transform:translate(47.6px,183.8px)}27.76%{opacity:1;transform:translate(46.3px,179.6px)}28.33%{opacity:1;transform:translate(45.4px,175.2px)}28.91%{opacity:1;transform:translate(45px,170.7px)}29.48%{opacity:1;transform:translate(45.1px,166.3px)}30.05%{opacity:1;transform:translate(45.8px,162px)}30.63%{opacity:0;transform:translate(47px,158px)}100%{opacity:0;transform:translate(47px,158px)}}.k5{animation:k5 16s linear infinite both}@keyframes k6{0%{opacity:0;transform:translate(47px,158px)}81.25%{opacity:0;transform:translate(47px,158px)}81.44%{opacity:1;transform:translate(47px,158px)}81.67%{opacity:1;transform:translate(45.8px,162px)}82.08%{opacity:1;transform:translate(45.1px,166.3px)}82.5%{opacity:1;transform:translate(45px,170.7px)}82.92%{opacity:1;transform:translate(45.4px,175.2px)}83.33%{opacity:1;transform:translate(46.3px,179.6px)}83.75%{opacity:1;transform:translate(47.6px,183.8px)}84.17%{opacity:1;transform:translate(49.4px,187.6px)}84.58%{opacity:1;transform:translate(51.6px,191.1px)}85%{opacity:1;transform:translate(54.2px,193.9px)}85.42%{opacity:1;transform:translate(57.1px,196.1px)}85.83%{opacity:1;transform:translate(60.4px,197.5px)}86.25%{opacity:0;transform:translate(64px,198px)}100%{opacity:0;transform:translate(64px,198px)}}.k6{animation:k6 16s linear infinite both}@keyframes k7{0%{opacity:0}31.25%{opacity:0}33.75%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k7{animation:k7 16s linear infinite both}@keyframes k8{0%{opacity:0}41.25%{opacity:0}43.75%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k8{animation:k8 16s linear infinite both}@keyframes k9{0%{opacity:0}63.75%{opacity:0}66.25%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k9{animation:k9 16s linear infinite both}@keyframes k10{0%{opacity:0}68.75%{opacity:0}71.25%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k10{animation:k10 16s linear infinite both}@keyframes k11{0%{opacity:0}81.25%{opacity:0}83.75%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k11{animation:k11 16s linear infinite both}@keyframes k12{0%{opacity:1}21.25%{opacity:1}23.75%{opacity:0}82.5%{opacity:0}85%{opacity:1}100%{opacity:1}}.k12{animation:k12 16s linear infinite both}@keyframes k13{0%{opacity:0}8.75%{opacity:0}10.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k13{animation:k13 16s linear infinite both}@keyframes k14{0%{opacity:0}88.75%{opacity:0}90.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k14{animation:k14 16s linear infinite both}@keyframes k15{0%{opacity:1}8.75%{opacity:1}10.62%{opacity:0}96.25%{opacity:0}100%{opacity:1}}.k15{animation:k15 16s linear infinite both}@keyframes k16{0%{opacity:0}8.75%{opacity:0}10.62%{opacity:1}88.75%{opacity:1}90.62%{opacity:0}100%{opacity:0}}.k16{animation:k16 16s linear infinite both}@keyframes k17{0%{opacity:0}88.75%{opacity:0}90.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k17{animation:k17 16s linear infinite both}@keyframes k18{0%{opacity:1}8.75%{opacity:1}10.62%{opacity:0}96.25%{opacity:0}100%{opacity:1}}.k18{animation:k18 16s linear infinite both}@keyframes k19{0%{opacity:0}8.75%{opacity:0}10.62%{opacity:1}88.75%{opacity:1}90.62%{opacity:0}100%{opacity:0}}.k19{animation:k19 16s linear infinite both}@keyframes k20{0%{opacity:0}88.75%{opacity:0}90.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k20{animation:k20 16s linear infinite both}@keyframes k21{0%{opacity:1}18.13%{opacity:1}20%{opacity:0}100%{opacity:0}}.k21{animation:k21 16s linear infinite both}@keyframes k22{0%{opacity:1}18.13%{opacity:1}20%{opacity:0.62}100%{opacity:0.62}}.k22{animation:k22 16s linear infinite both}@keyframes k23{0%{opacity:0}20%{opacity:0}21.88%{opacity:1}38.12%{opacity:1}40%{opacity:0}100%{opacity:0}}.k23{animation:k23 16s linear infinite both}@keyframes k24{0%{opacity:0.62}20%{opacity:0.62}21.88%{opacity:1.0}38.12%{opacity:1.0}40%{opacity:0.62}100%{opacity:0.62}}.k24{animation:k24 16s linear infinite both}@keyframes k25{0%{opacity:0}40%{opacity:0}41.88%{opacity:1}58.12%{opacity:1}60%{opacity:0}100%{opacity:0}}.k25{animation:k25 16s linear infinite both}@keyframes k26{0%{opacity:0.62}40%{opacity:0.62}41.88%{opacity:1.0}58.12%{opacity:1.0}60%{opacity:0.62}100%{opacity:0.62}}.k26{animation:k26 16s linear infinite both}@keyframes k27{0%{opacity:0}60%{opacity:0}61.88%{opacity:1}78.12%{opacity:1}80%{opacity:0}100%{opacity:0}}.k27{animation:k27 16s linear infinite both}@keyframes k28{0%{opacity:0.62}60%{opacity:0.62}61.88%{opacity:1.0}78.12%{opacity:1.0}80%{opacity:0.62}100%{opacity:0.62}}.k28{animation:k28 16s linear infinite both}@keyframes k29{0%{opacity:0}80%{opacity:0}81.88%{opacity:1}98.12%{opacity:1}100%{opacity:0}}.k29{animation:k29 16s linear infinite both}@keyframes k30{0%{opacity:0.62}80%{opacity:0.62}81.88%{opacity:1}98.12%{opacity:1}100%{opacity:0.62}}.k30{animation:k30 16s linear infinite both}@media (prefers-reduced-motion:reduce){*{animation:none!important}}</style><clipPath id="jack-clip"><rect width="240" height="240" rx="50"/></clipPath><symbol id="jack" viewBox="0 0 240 240"><rect width="240" height="240" rx="50" fill="#212E34"/><g clip-path="url(#jack-clip)"><g transform="translate(34 8)"><path fill="#4A4E51" d="M80 50.5c.2 .1 .2 .1 1 1c-.2 .6-.3 1.3-.5 2c-.7 .4-.7 .4-4 2c-6 3.3-6 3.3-7 6.5c1.2-.4 2.3-.6 3.5-1c.8 0 1.7 0 2.5 0c.2 .1 .2 .1 1 1c-.5 2-.5 2-5.5 6.5c-3 5-3 5-5.5 11c.2 .3 .2 .3 1 2c.2 .3 .3 .7 .5 1c-.7 .7-1.3 1.3-2 2c-1.3-.2-2.6-.3-4-.5c0-1.2 0-2.3 0-3.5c-.2 .1-.2 .1-1.5 .5c-1.2 2.7-2.4 5.3-3.5 8c-1 2.1-2 4.2-3 6.3c-2.5 5.7-2.5 5.7-3 8.2c.5 0 1 0 1.5 0c.1-.8 .4-1.7 .5-2.5c.6 0 1.3 0 2 0c.5 2 1 3.9 1.6 6c1 4 2.2 8 3.4 11.9c2.1 8.4 1.1 15.5-3 23.1c-.6-.2-1.3-.3-2-.5c-.1-1.5-.3-3-.5-4.6c-.3-2.6-.5-5.2-1-7.9c-.5 0-1 0-1.5 0c-.1 1.7-.4 3.3-.5 5c-2.7 10.2-2.7 10.2-6 13.5c-6.1-3.1-9-14.9-11-21c-1.1-8.2 .9-14.7 3.5-22.5c-8.4 8.3-8.3 20.5-9 31.5c-.6-.2-1.3-.3-2-.5c-5.4-8.9-5.1-15-3-25c.5-1 1-2 1.5-3c-3 1.5-3 1.5-6.5 5c-.4 0-.6 0-1 0c.8-6.5 3.2-10.2 7.5-15c.6-.8 .6-.8 3.5-5c2.6-2.6 5.2-5.1 8-7.5c.6-.6 .6-.6 3.5-3.5c-.5-2-.5-2-1-3.5c3.6-6 6.5-9.2 12.5-13c.4-.6 .6-1.3 1-2c-.5-.6-1-1.3-1.5-2c2-3 2-3 4.5-4.5c.5 1 .5 1 1 3.5c2.5-1 2.5-1 8.5-5.5c.3 0 .7 0 1 0c-.1-.4-.1-.4-.5-2.5c4.3-3.3 10-2.5 15-1zM153 69.5c1.2 .2 2.3 .3 3.5 .5c.3 .7 .7 1.3 1 2c.3 0 .7 0 1 0c.5 1.2 1 2.3 1.5 3.5c3.4 7.6 6.2 13.7 12.2 19.6c3.3 3.4 3.3 3.4 3.8 5.9c-.2 .2-.3 .3-.5 .5c.1 .7 .1 .7 .5 4.5c-.5 .3-1 .7-1.5 1c-.8-.3-1.7-.7-2.5-1c.3 .7 .7 1.3 1 2c1.2 5.8 1.4 10 1 16c-.7 0-1.3 0-2 0c-.4-1.3-.8-2.7-1.3-4.1c-2.1-6.1-5.2-11.4-8.7-16.9c-.2-.8-.2-.8-1.5-5c-.3-.6-.3-.6-2.1-3.5c-1.9-3.5-1.9-3.5-2.9-8.5c.7 0 1.3 0 2 0c.5 .5 1 1 1.5 1.5c-.8-2.3-1.7-4.6-2.5-7c-.7-2-1.3-4-2-6c-.7-1-1.3-2-2-3c.2-.7 .3-1.3 .5-2zM86.5 100.5c1 0 2 0 3 0c1.3 .8 2.7 1.7 4 2.5c-2.5 2.5-2.5 2.5-8 5.5c-5.5 4.3-8.6 7-10 14c-.8 2.2-1.7 4.3-2.5 6.5c-1.2 4.6-.5 9.3 0 14c-.5 0-1 0-1.5 0c-.2 2-.3 3.9-.5 6c-.7 0-1.3 0-2 0c-.3-.6-.3-.6-2-3.5c-1-.8-2-1.7-3-2.5c-.5-1.5-.5-1.5 1.5-8.5c.3-10.3-1-18.2-4-28c0-4 0-4 1-6c2 2 2 2 3 6.5c2.5-.5 2.5-.5 8-4c9.8-3.5 9.8-3.5 13-2.5zM43 161.5c2.5 2.5 2.5 2.5 3 5c-1.5 2.5-1.5 2.5-4 5c.5 3.5 .5 3.5 4 6.5c-.1 1.2-.4 2.3-.5 3.5c-5.3 3.5-5.3 3.5-8.5 3.5c-6-2.5-6-2.5-11.5-7c0-3 0-3 4-8c9.5-8.5 9.5-8.5 13.5-8.5z"/><path fill="#9B9D9E" d="M142.5 60.5c.5 1 .5 1-.5 4.5c1.2 1.5 2.3 3 3.5 4.5c2 4.5 2 4.5 2.5 9.5c-.5 .2-1 .3-1.5 .5c-.1 .2-.1 .2-.5 1c-2-1-3.9-2-6-3c-.8-.3-1.7-.7-2.5-1c-.3-.7-.7-1.3-1-2c.3-.5 .7-1 1-1.5c-.2-.7-.3-1.3-.5-2c-3.5 .5-3.5 .5-7.5 4c-.5 3-.5 3 0 5.5c-2 4-2 4-3.5 5.5c-.3 0-.7 0-1 0c0 .7 0 1.3 0 2c-2-1-2-1-2.5-2.5c.3-13.7 .3-13.7 4.8-18.5c4.1-3.9 6.3-4.8 11.7-6c1-.5 1-.5 3.5-.5zM98 70.5c.5 .3 1 .7 1.5 1c1.3 .3 2.7 .7 4 1c4.4 5.8 6.5 10.3 6.5 17.5c-2 2-2 2-5 2.5c-.3 .2-.3 .2-2 1c-.2-.2-.2-.2-1.5-1c.5-3 .5-3 1-5c-.7-.8-1.3-1.7-2-2.5c-.2-1.7-.3-3.3-.5-5c-4.5-2.7-8.5-3-13.5-2.5c-.1 .3-.1 .3-.5 1.5c1.3 .8 2.7 1.7 4 2.5c-.1 .4-.1 .4-.5 2.5c-2.3 1.2-4.6 2.3-7 3.5c-5.5 5.5-5.5 5.5-6.5 7c0 .5 0 1 0 1.5c.9-.1 .9-.1 5.5-.5c1 1.3 2 2.7 3 4c-.2 .5-.3 1-.5 1.5c-1.7 .5-3.2 1-5 1.6c-5.5 1.9-5.5 1.9-11 5.4c-.4 .1-.4 .1-2.5 .5c-1.5-1.5-1.5-1.5-2.5-6c-.4-.3-.6-.7-1-1c0-6.7 .9-11.7 3-18c.2-.6 .2-.6 1-3.5c7.8-9.7 20.5-14.1 32-9.5z"/><path fill="#FAF9F8" d="M103.5 40c0 .4 0 .6 0 1c2.2-.1 4.3-.4 6.5-.5c7 0 7 0 9.5 1c-.2 .6-.3 1.3-.5 2c-1.8 .6-3.7 1.3-5.5 2c-.2 .5-.3 1-.5 1.5c.7-.1 1.3-.4 2-.5c13.4-.3 13.4-.3 19.5 3.5c-.3 .6-.7 1.3-1 2c-1 0-2 0-3 0c0 .5 0 1 0 1.5c1.2 .1 1.2 .1 7 .5c3.5 2 3.5 2 5.5 4.5c0 1 0 2 0 3c-3.3 .9-6.7 1.6-10 2.5c-7.5 5-7.5 5-8.5 9c-.3 4.5-.4 9-.5 13.5c1.5 1 1.5 1 7.5 2.5c.9 .6 .9 .6 5.5 3.5c2 1 2 1 9 2.5c7.9 4 11.3 8.3 15 16.5c0 .5 0 1 0 1.5c-1 .2-2 .3-3 .5c.5 1.8 1 3.7 1.5 5.5c.3 8.5-.8 13.6-6 20.5c-.7-.2-1.3-.3-2-.5c0-1.7 0-3.3 0-5c-.7 .2-1.3 .3-2 .5c-1.3 2.7-2.7 5.3-4 8c-3.2 3.7-6.4 6.2-11 8c-1-1-1-1-1.5-3c-2.5 1.3-4.8 2.9-7 4.5c-6.3 2.8-10.8 3.2-17.6 1c-1.3-.5-2.6-1-3.9-1.5c.2 1.2 .3 2.3 .5 3.5c-.2 .3-.3 .7-.5 1c-5.9 0-9-1.8-14-5c-3-3.5-3-3.5-3-4.5c-.7 0-1.3 0-2 0c.5 2.2 1 4.3 1.5 6.5c-.3 .3-.7 .7-1 1c-4.5-.9-7.6-3.6-11-6.5c-4-6-4-9.3-4-16.5c2.5-7.5 2.5-7.5 3.5-9.5c.3-1.7 .7-3.3 1-5c6.4-8 14.7-13 24.5-16c6.4-6.3 6.4-6.3 8.5-10.5c0-6-1.6-9.8-4.5-15c-.2-.5-.3-1-.5-1.5c-1.7-.3-3.3-.7-5-1c-1.3-.7-2.7-1.3-4-2c-5.7-1.9-10.2-.3-15.5 1.5c-10 5.5-10 5.5-13 10c-.3-.2-.3-.2-2-1c1.3-6.8 4.6-11.7 9.5-16.5c.5-.3 1-.6 1.5-1c.2-.5 .3-1 .5-1.5c-1 .1-1 .1-6 1c-.3-.4-.7-.6-1-1c.5-2 .5-2 4-6c5-2.5 5-2.5 7.5-3c0-.5 0-1 0-1.5c-10.5-1.5-18.5 1-27 7c-.5 .9-1 1.6-1.5 2.5c-.9 .6-.9 .6-5.5 4c-4 3.5-7.6 7-11 11c-4.6 3.8-8.7 6.8-14 9.5c-.7-.3-1.3-.7-2-1c.2-.7 .3-1.3 .5-2c1.8-1 3.7-2 5.5-3c5.2-4.2 9.7-8.5 14-13.5c3.1-2.8 6.2-5.5 9.5-8c1.3-1.3 2.6-2.6 4-4c5.6-3.6 10.4-6.1 17-7c.5-1 1-2 1.5-3c7.2-2.4 12.5-2.2 20-.5c.2 .1 .2 .1 1 .5c3.5 0 3.5 0 8.5-3.5c1-.5 1-.5 3-.5zM118 92.5c-1 1.2-2 2.3-3 3.5c.5 2.5 .5 2.5 3 5c1.8 .8 3.7 1.7 5.5 2.5c.5 1 .5 1 .5 4.5c-4 4.8-6.1 5-12.5 6c0 .7 0 1.3 0 2c4.7 1.5 7.7 .2 12-1.5c1.2-.7 2.3-1.3 3.5-2c1.3 .3 2.7 .7 4 1c3.5 0 3.5 0 5.5-1.5c-.2-.5-.3-1-.5-1.5c-1.7-.3-3.3-.7-5-1c-2.5-2-2.5-2-3-5.5c2-3 2-3 4-5.5c.5-3.5 .5-3.5-1.5-6.5c-4.3-2.2-8.2-1-12.5 .5zM146 58.5c5.8 1.1 8.3 3.9 12.5 8c3.7 5.9 4.4 7.3 5 14c2.8 6.2 5.4 10 10.5 14.5c5.2 5.2 7.2 10.3 9 17.5c0 3.5 0 3.5-2 11c-.2 1.3-.2 1.3-1 7.5c-3.8 10.7-3.8 10.7-8.5 13.5c-.5 .2-.5 .2-3 1c-1 1.3-2 2.7-3 4c-4.5 3-4.5 3-10.5 5c-2.7-.6-5.3-1.3-8-2c-1.7 1.2-3.3 2.3-5 3.5c-2 1-2 1-4 .5c.5-1.5 .5-1.5 6-5c.3-.3 .3-.3 2-2c2.4 0 4.9 0 7.5 0c.5 .5 1 1 1.5 1.5c4.3-.6 7.1-2.3 10.5-5c3.5-5.5 3.5-5.5 4-7c1.5 0 2.9 0 4.5 0c4.3-8.7 4.6-14 2.5-23.5c-.3-1-.7-2-1-3c.3 .1 .3 .1 2 .5c1.5 2.5 1.5 2.5 1.5 4c.5-.2 1-.3 1.5-.5c-.8-10-4.3-16-11.4-22.8c-3.7-3.8-5.5-6.9-7.6-11.7c-.7-1.5-1.5-2.9-2.3-4.4c-1.7-3.6-1.7-3.6-1.7-5.6c.3-.2 .7-.3 1-.5c-.5-3-.5-3-4.5-7c-2-1-3.9-2-6-3c-.7-.4-1.3-.6-2-1c0-.6 0-1.3 0-2zM33.5 83.5c-1 4-5.1 6.4-8 9c-8.6 8.1-14.6 17.2-15.2 29.2c.3 8.1 .3 8.1 2.2 11.8c.5 0 1 0 1.5 0c.4-2.9 .6-6 1-9c.6 0 1.3 0 2 0c.2 1.3 .4 2.7 .6 4.1c1.8 9.3 5.3 14.8 12.9 20.4c2.3 1.2 4.6 2.3 7 3.5c.7 .6 .7 .6 4.5 3.5c3.5 0 3.5 0 8.5-1.5c1 0 2 0 3 0c-1 2-1 2-2.5 3.5c-6.5 1.7-9.6 .9-16-1.5c-1.1-1-2.3-2-3.5-3c-1.5-.7-2.9-1.3-4.5-2c-3.9-3.4-6.9-6.5-9.5-11c-1.1-.5-2.3-1-3.5-1.5c-5.9-6.7-8-12.7-8-21.5c2.4-12.2 7.2-19.8 16.5-28c4.5-4 4.5-4 6-5.5c3.5-1 3.5-1 5-.5zM50.5 172c2.5 1 2.5 1 7 6.5c6 6.2 12 11.8 18.8 17.1c5.7 4.5 11.3 9.1 16.7 13.9c1.3 1.2 2.7 2.3 4 3.5c.7 .7 1.3 1.3 2 2c-.2 .8-.3 1.7-.5 2.5c-6 2.9-11.7 4-18.5 4c-2.6-2.6-4.3-5.5-5.5-9c-.3 .1-.3 .1-1.5 .5c0 2.3 0 4.6 0 7c-.3 .5-.7 1-1 1.5c-13.3 0-24.6-4.6-34.5-13.5c-4.5-4.5-4.5-4.5-8.5-11c.5-2.5 .5-2.5 4.5-7c5-3 5-3 8-3.5c.5 .3 1 .7 1.5 1c2-.5 2-.5 4-2c.9-4.2 1.7-8.3 2.5-12.5c.4-.3 .6-.7 1-1z"/><path fill="#03B5BD" d="M58.5 149.5c2.5 .5 2.5 .5 7 3.5c9.8 5.2 18.9 7.9 30 8.5c2.2 .5 4.3 1 6.5 1.5c7 0 7 0 9-.5c2.3-.2 4.6-.3 7-.5c3.2-.9 6.3-1.9 9.5-3c1.3-.2 2.7-.3 4-.5c.2 .2 .3 .3 .5 .5c-.8 6.3-4.1 10.2-8 15c-.2 .5-.3 1-.5 1.5c1.7-1 3.3-2 5-3c.7 .2 1.3 .3 2 .5c-3 12.2-9.3 20.3-17.5 29.5c-.7 .7-.7 .7-4 4.5c-1 .8-2 1.7-3 2.5c-3.5-.5-3.5-.5-8-5c-2.3-1.8-4.7-3.7-7-5.5c-2.7-2.2-5.3-4.3-8-6.5c-.8-.6-.8-.6-4.5-3.5c-11-9.3-20.7-18.8-28.5-31c3-4 3-4 8.5-8.5z"/><ellipse cx="94" cy="94" rx="6.5" ry="6.5" fill="#212E34"/><ellipse cx="135" cy="85.6" rx="6.2" ry="5.8" fill="#212E34"/><g fill="#FAF9F8"><circle cx="91.5" cy="91.5" r="2.1"/><circle cx="134.6" cy="84.3" r="2"/></g><ellipse cx="124" cy="93.8" rx="3.4" ry="1.1" fill="#9B9D9E" transform="rotate(-6 124 93.8)"/></g></g><rect x="5" y="5" width="230" height="230" rx="45" fill="none"/></symbol></defs>
2
- <rect x="4" y="4" width="952" height="632" rx="18" fill="#FAF9F8" stroke="#C4CBCE" stroke-width="1.5"/>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="960" height="824" viewBox="0 0 960 824" role="img" aria-labelledby="t d"><title id="t">jackherd in herdr</title><desc id="d">Animated schematic of one herdr window, told in five numbered steps. 1, Lane search works: its state is a teal dot and its progress counts up. 2, It hits an open question: search turns to an amber diamond and sends the question as text. 3, The coordinator asks you: the question appears in the coordinator pane as a choice. 4, You pick: option 1 is selected. 5, The lane works on: the answer goes back to search, which is a teal dot again, and the herd counts one more box. On top of the herd, a sent mockup waits for you and the board's appointments list a release on Monday at 09:00.</desc><defs><style>.s{font-family:ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif}.m{font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace}@keyframes k1{0%{opacity:1.0}0%{opacity:1.0}38.12%{opacity:1.0}40%{opacity:0.42}80%{opacity:0.42}81.88%{opacity:1.0}100%{opacity:1.0}}.k1{animation:k1 16s linear infinite both}@keyframes k2{0%{opacity:0.42}40%{opacity:0.42}41.88%{opacity:1.0}81.88%{opacity:1.0}83.75%{opacity:0.42}100%{opacity:0.42}}.k2{animation:k2 16s linear infinite both}@keyframes k3{0%{opacity:0.42}100%{opacity:0.42}}.k3{animation:k3 16s linear infinite both}@keyframes k4{0%{opacity:0}21.25%{opacity:0}23.75%{opacity:1}82.5%{opacity:1}85%{opacity:0}100%{opacity:0}}.k4{animation:k4 16s linear infinite both}@keyframes k5{0%{opacity:0;transform:translate(64px,198px)}23.75%{opacity:0;transform:translate(64px,198px)}23.94%{opacity:1;transform:translate(64px,198px)}24.32%{opacity:1;transform:translate(60.4px,197.5px)}24.9%{opacity:1;transform:translate(57.1px,196.1px)}25.47%{opacity:1;transform:translate(54.2px,193.9px)}26.04%{opacity:1;transform:translate(51.6px,191.1px)}26.61%{opacity:1;transform:translate(49.4px,187.6px)}27.19%{opacity:1;transform:translate(47.6px,183.8px)}27.76%{opacity:1;transform:translate(46.3px,179.6px)}28.33%{opacity:1;transform:translate(45.4px,175.2px)}28.91%{opacity:1;transform:translate(45px,170.7px)}29.48%{opacity:1;transform:translate(45.1px,166.3px)}30.05%{opacity:1;transform:translate(45.8px,162px)}30.63%{opacity:0;transform:translate(47px,158px)}100%{opacity:0;transform:translate(47px,158px)}}.k5{animation:k5 16s linear infinite both}@keyframes k6{0%{opacity:0;transform:translate(47px,158px)}81.25%{opacity:0;transform:translate(47px,158px)}81.44%{opacity:1;transform:translate(47px,158px)}81.67%{opacity:1;transform:translate(45.8px,162px)}82.08%{opacity:1;transform:translate(45.1px,166.3px)}82.5%{opacity:1;transform:translate(45px,170.7px)}82.92%{opacity:1;transform:translate(45.4px,175.2px)}83.33%{opacity:1;transform:translate(46.3px,179.6px)}83.75%{opacity:1;transform:translate(47.6px,183.8px)}84.17%{opacity:1;transform:translate(49.4px,187.6px)}84.58%{opacity:1;transform:translate(51.6px,191.1px)}85%{opacity:1;transform:translate(54.2px,193.9px)}85.42%{opacity:1;transform:translate(57.1px,196.1px)}85.83%{opacity:1;transform:translate(60.4px,197.5px)}86.25%{opacity:0;transform:translate(64px,198px)}100%{opacity:0;transform:translate(64px,198px)}}.k6{animation:k6 16s linear infinite both}@keyframes k7{0%{opacity:0}31.25%{opacity:0}33.75%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k7{animation:k7 16s linear infinite both}@keyframes k8{0%{opacity:0}41.25%{opacity:0}43.75%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k8{animation:k8 16s linear infinite both}@keyframes k9{0%{opacity:0}63.75%{opacity:0}66.25%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k9{animation:k9 16s linear infinite both}@keyframes k10{0%{opacity:0}68.75%{opacity:0}71.25%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k10{animation:k10 16s linear infinite both}@keyframes k11{0%{opacity:0}81.25%{opacity:0}83.75%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k11{animation:k11 16s linear infinite both}@keyframes k12{0%{opacity:1}21.25%{opacity:1}23.75%{opacity:0}82.5%{opacity:0}85%{opacity:1}100%{opacity:1}}.k12{animation:k12 16s linear infinite both}@keyframes k13{0%{opacity:0}8.75%{opacity:0}10.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k13{animation:k13 16s linear infinite both}@keyframes k14{0%{opacity:0}88.75%{opacity:0}90.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k14{animation:k14 16s linear infinite both}@keyframes k15{0%{opacity:1}8.75%{opacity:1}10.62%{opacity:0}96.25%{opacity:0}100%{opacity:1}}.k15{animation:k15 16s linear infinite both}@keyframes k16{0%{opacity:0}8.75%{opacity:0}10.62%{opacity:1}88.75%{opacity:1}90.62%{opacity:0}100%{opacity:0}}.k16{animation:k16 16s linear infinite both}@keyframes k17{0%{opacity:0}88.75%{opacity:0}90.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k17{animation:k17 16s linear infinite both}@keyframes k18{0%{opacity:1}8.75%{opacity:1}10.62%{opacity:0}96.25%{opacity:0}100%{opacity:1}}.k18{animation:k18 16s linear infinite both}@keyframes k19{0%{opacity:0}8.75%{opacity:0}10.62%{opacity:1}88.75%{opacity:1}90.62%{opacity:0}100%{opacity:0}}.k19{animation:k19 16s linear infinite both}@keyframes k20{0%{opacity:0}88.75%{opacity:0}90.62%{opacity:1}96.25%{opacity:1}100%{opacity:0}}.k20{animation:k20 16s linear infinite both}@keyframes k21{0%{opacity:1}18.13%{opacity:1}20%{opacity:0}100%{opacity:0}}.k21{animation:k21 16s linear infinite both}@keyframes k22{0%{opacity:1}18.13%{opacity:1}20%{opacity:0.62}100%{opacity:0.62}}.k22{animation:k22 16s linear infinite both}@keyframes k23{0%{opacity:0}20%{opacity:0}21.88%{opacity:1}38.12%{opacity:1}40%{opacity:0}100%{opacity:0}}.k23{animation:k23 16s linear infinite both}@keyframes k24{0%{opacity:0.62}20%{opacity:0.62}21.88%{opacity:1.0}38.12%{opacity:1.0}40%{opacity:0.62}100%{opacity:0.62}}.k24{animation:k24 16s linear infinite both}@keyframes k25{0%{opacity:0}40%{opacity:0}41.88%{opacity:1}58.12%{opacity:1}60%{opacity:0}100%{opacity:0}}.k25{animation:k25 16s linear infinite both}@keyframes k26{0%{opacity:0.62}40%{opacity:0.62}41.88%{opacity:1.0}58.12%{opacity:1.0}60%{opacity:0.62}100%{opacity:0.62}}.k26{animation:k26 16s linear infinite both}@keyframes k27{0%{opacity:0}60%{opacity:0}61.88%{opacity:1}78.12%{opacity:1}80%{opacity:0}100%{opacity:0}}.k27{animation:k27 16s linear infinite both}@keyframes k28{0%{opacity:0.62}60%{opacity:0.62}61.88%{opacity:1.0}78.12%{opacity:1.0}80%{opacity:0.62}100%{opacity:0.62}}.k28{animation:k28 16s linear infinite both}@keyframes k29{0%{opacity:0}80%{opacity:0}81.88%{opacity:1}98.12%{opacity:1}100%{opacity:0}}.k29{animation:k29 16s linear infinite both}@keyframes k30{0%{opacity:0.62}80%{opacity:0.62}81.88%{opacity:1}98.12%{opacity:1}100%{opacity:0.62}}.k30{animation:k30 16s linear infinite both}@media (prefers-reduced-motion:reduce){*{animation:none!important}}</style><clipPath id="jack-clip"><rect width="240" height="240" rx="50"/></clipPath><symbol id="jack" viewBox="0 0 240 240"><rect width="240" height="240" rx="50" fill="#212E34"/><g clip-path="url(#jack-clip)"><g transform="translate(34 8)"><path fill="#4A4E51" d="M80 50.5c.2 .1 .2 .1 1 1c-.2 .6-.3 1.3-.5 2c-.7 .4-.7 .4-4 2c-6 3.3-6 3.3-7 6.5c1.2-.4 2.3-.6 3.5-1c.8 0 1.7 0 2.5 0c.2 .1 .2 .1 1 1c-.5 2-.5 2-5.5 6.5c-3 5-3 5-5.5 11c.2 .3 .2 .3 1 2c.2 .3 .3 .7 .5 1c-.7 .7-1.3 1.3-2 2c-1.3-.2-2.6-.3-4-.5c0-1.2 0-2.3 0-3.5c-.2 .1-.2 .1-1.5 .5c-1.2 2.7-2.4 5.3-3.5 8c-1 2.1-2 4.2-3 6.3c-2.5 5.7-2.5 5.7-3 8.2c.5 0 1 0 1.5 0c.1-.8 .4-1.7 .5-2.5c.6 0 1.3 0 2 0c.5 2 1 3.9 1.6 6c1 4 2.2 8 3.4 11.9c2.1 8.4 1.1 15.5-3 23.1c-.6-.2-1.3-.3-2-.5c-.1-1.5-.3-3-.5-4.6c-.3-2.6-.5-5.2-1-7.9c-.5 0-1 0-1.5 0c-.1 1.7-.4 3.3-.5 5c-2.7 10.2-2.7 10.2-6 13.5c-6.1-3.1-9-14.9-11-21c-1.1-8.2 .9-14.7 3.5-22.5c-8.4 8.3-8.3 20.5-9 31.5c-.6-.2-1.3-.3-2-.5c-5.4-8.9-5.1-15-3-25c.5-1 1-2 1.5-3c-3 1.5-3 1.5-6.5 5c-.4 0-.6 0-1 0c.8-6.5 3.2-10.2 7.5-15c.6-.8 .6-.8 3.5-5c2.6-2.6 5.2-5.1 8-7.5c.6-.6 .6-.6 3.5-3.5c-.5-2-.5-2-1-3.5c3.6-6 6.5-9.2 12.5-13c.4-.6 .6-1.3 1-2c-.5-.6-1-1.3-1.5-2c2-3 2-3 4.5-4.5c.5 1 .5 1 1 3.5c2.5-1 2.5-1 8.5-5.5c.3 0 .7 0 1 0c-.1-.4-.1-.4-.5-2.5c4.3-3.3 10-2.5 15-1zM153 69.5c1.2 .2 2.3 .3 3.5 .5c.3 .7 .7 1.3 1 2c.3 0 .7 0 1 0c.5 1.2 1 2.3 1.5 3.5c3.4 7.6 6.2 13.7 12.2 19.6c3.3 3.4 3.3 3.4 3.8 5.9c-.2 .2-.3 .3-.5 .5c.1 .7 .1 .7 .5 4.5c-.5 .3-1 .7-1.5 1c-.8-.3-1.7-.7-2.5-1c.3 .7 .7 1.3 1 2c1.2 5.8 1.4 10 1 16c-.7 0-1.3 0-2 0c-.4-1.3-.8-2.7-1.3-4.1c-2.1-6.1-5.2-11.4-8.7-16.9c-.2-.8-.2-.8-1.5-5c-.3-.6-.3-.6-2.1-3.5c-1.9-3.5-1.9-3.5-2.9-8.5c.7 0 1.3 0 2 0c.5 .5 1 1 1.5 1.5c-.8-2.3-1.7-4.6-2.5-7c-.7-2-1.3-4-2-6c-.7-1-1.3-2-2-3c.2-.7 .3-1.3 .5-2zM86.5 100.5c1 0 2 0 3 0c1.3 .8 2.7 1.7 4 2.5c-2.5 2.5-2.5 2.5-8 5.5c-5.5 4.3-8.6 7-10 14c-.8 2.2-1.7 4.3-2.5 6.5c-1.2 4.6-.5 9.3 0 14c-.5 0-1 0-1.5 0c-.2 2-.3 3.9-.5 6c-.7 0-1.3 0-2 0c-.3-.6-.3-.6-2-3.5c-1-.8-2-1.7-3-2.5c-.5-1.5-.5-1.5 1.5-8.5c.3-10.3-1-18.2-4-28c0-4 0-4 1-6c2 2 2 2 3 6.5c2.5-.5 2.5-.5 8-4c9.8-3.5 9.8-3.5 13-2.5zM43 161.5c2.5 2.5 2.5 2.5 3 5c-1.5 2.5-1.5 2.5-4 5c.5 3.5 .5 3.5 4 6.5c-.1 1.2-.4 2.3-.5 3.5c-5.3 3.5-5.3 3.5-8.5 3.5c-6-2.5-6-2.5-11.5-7c0-3 0-3 4-8c9.5-8.5 9.5-8.5 13.5-8.5z"/><path fill="#9B9D9E" d="M142.5 60.5c.5 1 .5 1-.5 4.5c1.2 1.5 2.3 3 3.5 4.5c2 4.5 2 4.5 2.5 9.5c-.5 .2-1 .3-1.5 .5c-.1 .2-.1 .2-.5 1c-2-1-3.9-2-6-3c-.8-.3-1.7-.7-2.5-1c-.3-.7-.7-1.3-1-2c.3-.5 .7-1 1-1.5c-.2-.7-.3-1.3-.5-2c-3.5 .5-3.5 .5-7.5 4c-.5 3-.5 3 0 5.5c-2 4-2 4-3.5 5.5c-.3 0-.7 0-1 0c0 .7 0 1.3 0 2c-2-1-2-1-2.5-2.5c.3-13.7 .3-13.7 4.8-18.5c4.1-3.9 6.3-4.8 11.7-6c1-.5 1-.5 3.5-.5zM98 70.5c.5 .3 1 .7 1.5 1c1.3 .3 2.7 .7 4 1c4.4 5.8 6.5 10.3 6.5 17.5c-2 2-2 2-5 2.5c-.3 .2-.3 .2-2 1c-.2-.2-.2-.2-1.5-1c.5-3 .5-3 1-5c-.7-.8-1.3-1.7-2-2.5c-.2-1.7-.3-3.3-.5-5c-4.5-2.7-8.5-3-13.5-2.5c-.1 .3-.1 .3-.5 1.5c1.3 .8 2.7 1.7 4 2.5c-.1 .4-.1 .4-.5 2.5c-2.3 1.2-4.6 2.3-7 3.5c-5.5 5.5-5.5 5.5-6.5 7c0 .5 0 1 0 1.5c.9-.1 .9-.1 5.5-.5c1 1.3 2 2.7 3 4c-.2 .5-.3 1-.5 1.5c-1.7 .5-3.2 1-5 1.6c-5.5 1.9-5.5 1.9-11 5.4c-.4 .1-.4 .1-2.5 .5c-1.5-1.5-1.5-1.5-2.5-6c-.4-.3-.6-.7-1-1c0-6.7 .9-11.7 3-18c.2-.6 .2-.6 1-3.5c7.8-9.7 20.5-14.1 32-9.5z"/><path fill="#FAF9F8" d="M103.5 40c0 .4 0 .6 0 1c2.2-.1 4.3-.4 6.5-.5c7 0 7 0 9.5 1c-.2 .6-.3 1.3-.5 2c-1.8 .6-3.7 1.3-5.5 2c-.2 .5-.3 1-.5 1.5c.7-.1 1.3-.4 2-.5c13.4-.3 13.4-.3 19.5 3.5c-.3 .6-.7 1.3-1 2c-1 0-2 0-3 0c0 .5 0 1 0 1.5c1.2 .1 1.2 .1 7 .5c3.5 2 3.5 2 5.5 4.5c0 1 0 2 0 3c-3.3 .9-6.7 1.6-10 2.5c-7.5 5-7.5 5-8.5 9c-.3 4.5-.4 9-.5 13.5c1.5 1 1.5 1 7.5 2.5c.9 .6 .9 .6 5.5 3.5c2 1 2 1 9 2.5c7.9 4 11.3 8.3 15 16.5c0 .5 0 1 0 1.5c-1 .2-2 .3-3 .5c.5 1.8 1 3.7 1.5 5.5c.3 8.5-.8 13.6-6 20.5c-.7-.2-1.3-.3-2-.5c0-1.7 0-3.3 0-5c-.7 .2-1.3 .3-2 .5c-1.3 2.7-2.7 5.3-4 8c-3.2 3.7-6.4 6.2-11 8c-1-1-1-1-1.5-3c-2.5 1.3-4.8 2.9-7 4.5c-6.3 2.8-10.8 3.2-17.6 1c-1.3-.5-2.6-1-3.9-1.5c.2 1.2 .3 2.3 .5 3.5c-.2 .3-.3 .7-.5 1c-5.9 0-9-1.8-14-5c-3-3.5-3-3.5-3-4.5c-.7 0-1.3 0-2 0c.5 2.2 1 4.3 1.5 6.5c-.3 .3-.7 .7-1 1c-4.5-.9-7.6-3.6-11-6.5c-4-6-4-9.3-4-16.5c2.5-7.5 2.5-7.5 3.5-9.5c.3-1.7 .7-3.3 1-5c6.4-8 14.7-13 24.5-16c6.4-6.3 6.4-6.3 8.5-10.5c0-6-1.6-9.8-4.5-15c-.2-.5-.3-1-.5-1.5c-1.7-.3-3.3-.7-5-1c-1.3-.7-2.7-1.3-4-2c-5.7-1.9-10.2-.3-15.5 1.5c-10 5.5-10 5.5-13 10c-.3-.2-.3-.2-2-1c1.3-6.8 4.6-11.7 9.5-16.5c.5-.3 1-.6 1.5-1c.2-.5 .3-1 .5-1.5c-1 .1-1 .1-6 1c-.3-.4-.7-.6-1-1c.5-2 .5-2 4-6c5-2.5 5-2.5 7.5-3c0-.5 0-1 0-1.5c-10.5-1.5-18.5 1-27 7c-.5 .9-1 1.6-1.5 2.5c-.9 .6-.9 .6-5.5 4c-4 3.5-7.6 7-11 11c-4.6 3.8-8.7 6.8-14 9.5c-.7-.3-1.3-.7-2-1c.2-.7 .3-1.3 .5-2c1.8-1 3.7-2 5.5-3c5.2-4.2 9.7-8.5 14-13.5c3.1-2.8 6.2-5.5 9.5-8c1.3-1.3 2.6-2.6 4-4c5.6-3.6 10.4-6.1 17-7c.5-1 1-2 1.5-3c7.2-2.4 12.5-2.2 20-.5c.2 .1 .2 .1 1 .5c3.5 0 3.5 0 8.5-3.5c1-.5 1-.5 3-.5zM118 92.5c-1 1.2-2 2.3-3 3.5c.5 2.5 .5 2.5 3 5c1.8 .8 3.7 1.7 5.5 2.5c.5 1 .5 1 .5 4.5c-4 4.8-6.1 5-12.5 6c0 .7 0 1.3 0 2c4.7 1.5 7.7 .2 12-1.5c1.2-.7 2.3-1.3 3.5-2c1.3 .3 2.7 .7 4 1c3.5 0 3.5 0 5.5-1.5c-.2-.5-.3-1-.5-1.5c-1.7-.3-3.3-.7-5-1c-2.5-2-2.5-2-3-5.5c2-3 2-3 4-5.5c.5-3.5 .5-3.5-1.5-6.5c-4.3-2.2-8.2-1-12.5 .5zM146 58.5c5.8 1.1 8.3 3.9 12.5 8c3.7 5.9 4.4 7.3 5 14c2.8 6.2 5.4 10 10.5 14.5c5.2 5.2 7.2 10.3 9 17.5c0 3.5 0 3.5-2 11c-.2 1.3-.2 1.3-1 7.5c-3.8 10.7-3.8 10.7-8.5 13.5c-.5 .2-.5 .2-3 1c-1 1.3-2 2.7-3 4c-4.5 3-4.5 3-10.5 5c-2.7-.6-5.3-1.3-8-2c-1.7 1.2-3.3 2.3-5 3.5c-2 1-2 1-4 .5c.5-1.5 .5-1.5 6-5c.3-.3 .3-.3 2-2c2.4 0 4.9 0 7.5 0c.5 .5 1 1 1.5 1.5c4.3-.6 7.1-2.3 10.5-5c3.5-5.5 3.5-5.5 4-7c1.5 0 2.9 0 4.5 0c4.3-8.7 4.6-14 2.5-23.5c-.3-1-.7-2-1-3c.3 .1 .3 .1 2 .5c1.5 2.5 1.5 2.5 1.5 4c.5-.2 1-.3 1.5-.5c-.8-10-4.3-16-11.4-22.8c-3.7-3.8-5.5-6.9-7.6-11.7c-.7-1.5-1.5-2.9-2.3-4.4c-1.7-3.6-1.7-3.6-1.7-5.6c.3-.2 .7-.3 1-.5c-.5-3-.5-3-4.5-7c-2-1-3.9-2-6-3c-.7-.4-1.3-.6-2-1c0-.6 0-1.3 0-2zM33.5 83.5c-1 4-5.1 6.4-8 9c-8.6 8.1-14.6 17.2-15.2 29.2c.3 8.1 .3 8.1 2.2 11.8c.5 0 1 0 1.5 0c.4-2.9 .6-6 1-9c.6 0 1.3 0 2 0c.2 1.3 .4 2.7 .6 4.1c1.8 9.3 5.3 14.8 12.9 20.4c2.3 1.2 4.6 2.3 7 3.5c.7 .6 .7 .6 4.5 3.5c3.5 0 3.5 0 8.5-1.5c1 0 2 0 3 0c-1 2-1 2-2.5 3.5c-6.5 1.7-9.6 .9-16-1.5c-1.1-1-2.3-2-3.5-3c-1.5-.7-2.9-1.3-4.5-2c-3.9-3.4-6.9-6.5-9.5-11c-1.1-.5-2.3-1-3.5-1.5c-5.9-6.7-8-12.7-8-21.5c2.4-12.2 7.2-19.8 16.5-28c4.5-4 4.5-4 6-5.5c3.5-1 3.5-1 5-.5zM50.5 172c2.5 1 2.5 1 7 6.5c6 6.2 12 11.8 18.8 17.1c5.7 4.5 11.3 9.1 16.7 13.9c1.3 1.2 2.7 2.3 4 3.5c.7 .7 1.3 1.3 2 2c-.2 .8-.3 1.7-.5 2.5c-6 2.9-11.7 4-18.5 4c-2.6-2.6-4.3-5.5-5.5-9c-.3 .1-.3 .1-1.5 .5c0 2.3 0 4.6 0 7c-.3 .5-.7 1-1 1.5c-13.3 0-24.6-4.6-34.5-13.5c-4.5-4.5-4.5-4.5-8.5-11c.5-2.5 .5-2.5 4.5-7c5-3 5-3 8-3.5c.5 .3 1 .7 1.5 1c2-.5 2-.5 4-2c.9-4.2 1.7-8.3 2.5-12.5c.4-.3 .6-.7 1-1z"/><path fill="#03B5BD" d="M58.5 149.5c2.5 .5 2.5 .5 7 3.5c9.8 5.2 18.9 7.9 30 8.5c2.2 .5 4.3 1 6.5 1.5c7 0 7 0 9-.5c2.3-.2 4.6-.3 7-.5c3.2-.9 6.3-1.9 9.5-3c1.3-.2 2.7-.3 4-.5c.2 .2 .3 .3 .5 .5c-.8 6.3-4.1 10.2-8 15c-.2 .5-.3 1-.5 1.5c1.7-1 3.3-2 5-3c.7 .2 1.3 .3 2 .5c-3 12.2-9.3 20.3-17.5 29.5c-.7 .7-.7 .7-4 4.5c-1 .8-2 1.7-3 2.5c-3.5-.5-3.5-.5-8-5c-2.3-1.8-4.7-3.7-7-5.5c-2.7-2.2-5.3-4.3-8-6.5c-.8-.6-.8-.6-4.5-3.5c-11-9.3-20.7-18.8-28.5-31c3-4 3-4 8.5-8.5z"/><ellipse cx="94" cy="94" rx="6.5" ry="6.5" fill="#212E34"/><ellipse cx="135" cy="85.6" rx="6.2" ry="5.8" fill="#212E34"/><g fill="#FAF9F8"><circle cx="91.5" cy="91.5" r="2.1"/><circle cx="134.6" cy="84.3" r="2"/></g><ellipse cx="124" cy="93.8" rx="3.4" ry="1.1" fill="#9B9D9E" transform="rotate(-6 124 93.8)"/></g></g><rect x="5" y="5" width="230" height="230" rx="45" fill="none"/></symbol></defs>
2
+ <rect x="4" y="4" width="952" height="732" rx="18" fill="#FAF9F8" stroke="#C4CBCE" stroke-width="1.5"/>
3
3
  <path d="M4 22a18 18 0 0 1 18-18h916a18 18 0 0 1 18 18v24H4z" fill="#212E34"/>
4
4
  <circle cx="26" cy="25" r="5" fill="#C4CBCE" opacity="0.55"/>
5
5
  <circle cx="44" cy="25" r="5" fill="#C4CBCE" opacity="0.43"/>
6
6
  <circle cx="62" cy="25" r="5" fill="#C4CBCE" opacity="0.31"/>
7
7
  <text x="82" y="30" class="m" font-size="13" fill="#FAF9F8" font-weight="700">herdr</text>
8
8
  <text x="936" y="30" class="m" font-size="12" fill="#C4CBCE" text-anchor="end">/jh-plan writes the plan · its board lists the lanes · /jh-orchestrate starts them</text>
9
- <rect x="16" y="58" width="190" height="566" rx="12" fill="#FFFFFF" stroke="#DDE2E4" stroke-width="1.2"/>
10
- <rect x="218" y="58" width="252" height="566" rx="12" fill="#FFFFFF" stroke="#DDE2E4" stroke-width="1.2"/>
11
- <rect x="482" y="58" width="462" height="566" rx="12" fill="#FFFFFF" stroke="#DDE2E4" stroke-width="1.2"/>
9
+ <rect x="16" y="58" width="190" height="666" rx="12" fill="#FFFFFF" stroke="#DDE2E4" stroke-width="1.2"/>
10
+ <rect x="218" y="58" width="252" height="666" rx="12" fill="#FFFFFF" stroke="#DDE2E4" stroke-width="1.2"/>
11
+ <rect x="482" y="58" width="462" height="666" rx="12" fill="#FFFFFF" stroke="#DDE2E4" stroke-width="1.2"/>
12
12
  <g class="k1">
13
13
  <text x="32" y="84" class="s" font-size="11" fill="#4A4E51" font-weight="600" letter-spacing="1.2">WORKSPACES</text>
14
14
  <text x="32" y="108" class="m" font-size="13" fill="#212E34" font-weight="700">▾ shop</text>
@@ -63,76 +63,80 @@
63
63
  <ellipse cx="506" cy="123.5" rx="4" ry="3.2" fill="#03B5BD"/><ellipse cx="501.1" cy="119.1" rx="1.7" ry="2.2" fill="#03B5BD"/><ellipse cx="504.2" cy="116.1" rx="1.7" ry="2.2" fill="#03B5BD"/><ellipse cx="507.8" cy="116.1" rx="1.7" ry="2.2" fill="#03B5BD"/><ellipse cx="510.9" cy="119.1" rx="1.7" ry="2.2" fill="#03B5BD"/>
64
64
  <text x="520" y="126" class="m" font-size="13" fill="#212E34" font-weight="700">Board 01</text>
65
65
  <text x="928" y="126" class="m" font-size="12.5" fill="#4A4E51" text-anchor="end">11:38</text>
66
- <path d="M498 140h430" stroke="#DDE2E4" stroke-width="1"/>
66
+ <text x="498" y="152" class="m" font-size="11" fill="#4A4E51">▾</text><text x="514" y="152" class="m" font-size="12.5" fill="#212E34" font-weight="600">Waiting for you</text><text x="634" y="152" class="m" font-size="12.5" fill="#9A5A00" font-weight="700">· 1</text><path d="M666 148H928" stroke="#DDE2E4" stroke-width="1"/>
67
+ <text x="498" y="174" class="m" font-size="12.5" fill="#7E8487">▌</text><text x="514" y="174" class="m" font-size="12.5" fill="#212E34">📎</text><text x="534" y="174" class="m" font-size="12.5" fill="#212E34" font-weight="600">search-box-mockup.html</text>
68
+ <text x="498" y="202" class="m" font-size="11" fill="#4A4E51">▾</text><text x="514" y="202" class="m" font-size="12.5" fill="#212E34" font-weight="600">Appointments</text><text x="611.2" y="202" class="m" font-size="12.5" fill="#9A5A00" font-weight="700">· 1</text><path d="M643.2 198H928" stroke="#DDE2E4" stroke-width="1"/>
69
+ <text x="498" y="224" class="m" font-size="12.5" fill="#7E8487">▌</text><text x="514" y="224" class="m" font-size="12.5" fill="#4A4E51">◷</text><text x="534" y="224" class="m" font-size="12.5" fill="#212E34" font-weight="600">Mo 09:00 <tspan font-weight="400" fill="#4A4E51">Release 1.4 to production</tspan></text>
70
+ <path d="M498 240h430" stroke="#DDE2E4" stroke-width="1"/>
67
71
  <g class="k1">
68
- <text x="498" y="170" class="m" font-size="11" fill="#4A4E51">▾</text><text x="528" y="170" class="m" font-size="13" fill="#212E34" font-weight="700">search</text><path d="M709.5 163.5h13a6.5 5.5 0 0 1-13 0Z" fill="#4A4E51"/><path d="M712 170h8" stroke="#4A4E51" stroke-width="1.6" stroke-linecap="round"/><text x="760" y="170" class="m" font-size="12.5" fill="#4A4E51" text-anchor="end">62 %</text><path d="M772 158v15" stroke="#C4CBCE" stroke-width="1.2"/><circle cx="880" cy="165.5" r="5.2" fill="none" stroke="#4A4E51" stroke-width="1.4"/><path d="M880 160.3A5.2 5.2 0 0 0 880 170.7Z" fill="#4A4E51"/>
69
- <circle class="k12" cx="513" cy="165.5" r="4.8" fill="#03B5BD"/>
70
- <path class="k4" opacity="0" d="M513 159.1L519.4 165.5L513 171.9L506.6 165.5Z" fill="#E8A33D"/>
71
- <text x="640" y="170" class="m k12" font-size="12.5" fill="#03777D" font-weight="600">working</text>
72
- <text x="640" y="170" class="m k4" font-size="12.5" fill="#9A5A00" font-weight="800" opacity="0">blocked</text>
73
- <rect x="786" y="160" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="794" y="160" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="802" y="160" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="810" y="160" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="818" y="160" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="826" y="160" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="834" y="160" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="842" y="160" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="850" y="160" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="858" y="160" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/>
74
- <rect class="k13" x="810" y="160" width="6.5" height="11" rx="1.2" fill="#03B5BD"/>
75
- <rect class="k14" x="818" y="160" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect class="k14" x="826" y="160" width="6.5" height="11" rx="1.2" fill="#03B5BD"/>
76
- <text x="928" y="170" class="m k15" font-size="12.5" fill="#4A4E51" text-anchor="end" opacity="0">2/7</text><text x="928" y="170" class="m k16" font-size="12.5" fill="#212E34" font-weight="700" text-anchor="end" opacity="0">3/7</text><text x="928" y="170" class="m k17" font-size="12.5" fill="#03777D" font-weight="700" text-anchor="end">4/7</text>
77
- <text x="528" y="192" class="m" font-size="12" fill="#4A4E51">⎇ feat/search <tspan fill="#03777D">↑1</tspan> ±4</text>
78
- <text x="498" y="216" class="m" font-size="12.5" fill="#7E8487">└─</text><text x="524" y="216" class="s" font-size="13" fill="#212E34" font-weight="700">Product search</text>
79
- <text x="524" y="238" class="m" font-size="12.5" fill="#7E8487">├─</text><path d="M552 234l3 3.2 5.6-6.4" fill="none" stroke="#2F9E44" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/><text x="568" y="238" class="m" font-size="12.5" fill="#212E34">P1 Index</text>
80
- <text x="928" y="238" class="m" font-size="12.5" fill="#2F9E44" font-weight="600" text-anchor="end">2/2</text>
81
- <text x="524" y="260" class="m" font-size="12.5" fill="#7E8487">├─</text><text x="552" y="260" class="m" font-size="12.5" fill="#212E34">▸ P2 Search box</text>
82
- <text x="928" y="260" class="m k18" font-size="12.5" fill="#4A4E51" text-anchor="end" opacity="0">0/4</text><text x="928" y="260" class="m k19" font-size="12.5" fill="#212E34" font-weight="700" text-anchor="end" opacity="0">1/4</text><text x="928" y="260" class="m k20" font-size="12.5" fill="#03777D" font-weight="700" text-anchor="end">2/4</text>
83
- <text x="524" y="280" class="m" font-size="12.5" fill="#7E8487">│ └─</text><text x="574" y="280" class="m" font-size="12" fill="#212E34" font-weight="600"><tspan fill="#03777D">▶</tspan> wire the box to the index</text>
84
- <text x="524" y="302" class="m" font-size="12.5" fill="#7E8487">└─</text><text x="552" y="302" class="m" font-size="12.5" fill="#212E34">▸ P3 Docs</text>
85
- <text x="928" y="302" class="m" font-size="12.5" fill="#4A4E51" text-anchor="end">0/1</text>
72
+ <text x="498" y="270" class="m" font-size="11" fill="#4A4E51">▾</text><text x="528" y="270" class="m" font-size="13" fill="#212E34" font-weight="700">search</text><path d="M709.5 263.5h13a6.5 5.5 0 0 1-13 0Z" fill="#4A4E51"/><path d="M712 270h8" stroke="#4A4E51" stroke-width="1.6" stroke-linecap="round"/><text x="760" y="270" class="m" font-size="12.5" fill="#4A4E51" text-anchor="end">62 %</text><path d="M772 258v15" stroke="#C4CBCE" stroke-width="1.2"/><circle cx="880" cy="265.5" r="5.2" fill="none" stroke="#4A4E51" stroke-width="1.4"/><path d="M880 260.3A5.2 5.2 0 0 0 880 270.7Z" fill="#4A4E51"/>
73
+ <circle class="k12" cx="513" cy="265.5" r="4.8" fill="#03B5BD"/>
74
+ <path class="k4" opacity="0" d="M513 259.1L519.4 265.5L513 271.9L506.6 265.5Z" fill="#E8A33D"/>
75
+ <text x="640" y="270" class="m k12" font-size="12.5" fill="#03777D" font-weight="600">working</text>
76
+ <text x="640" y="270" class="m k4" font-size="12.5" fill="#9A5A00" font-weight="800" opacity="0">blocked</text>
77
+ <rect x="786" y="260" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="794" y="260" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="802" y="260" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="810" y="260" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="818" y="260" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="826" y="260" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="834" y="260" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="842" y="260" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="850" y="260" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="858" y="260" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/>
78
+ <rect class="k13" x="810" y="260" width="6.5" height="11" rx="1.2" fill="#03B5BD"/>
79
+ <rect class="k14" x="818" y="260" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect class="k14" x="826" y="260" width="6.5" height="11" rx="1.2" fill="#03B5BD"/>
80
+ <text x="928" y="270" class="m k15" font-size="12.5" fill="#4A4E51" text-anchor="end" opacity="0">2/7</text><text x="928" y="270" class="m k16" font-size="12.5" fill="#212E34" font-weight="700" text-anchor="end" opacity="0">3/7</text><text x="928" y="270" class="m k17" font-size="12.5" fill="#03777D" font-weight="700" text-anchor="end">4/7</text>
81
+ <text x="528" y="292" class="m" font-size="12" fill="#4A4E51">⎇ feat/search <tspan fill="#03777D">↑1</tspan> ±4</text>
82
+ <text x="498" y="316" class="m" font-size="12.5" fill="#7E8487">└─</text><text x="524" y="316" class="s" font-size="13" fill="#212E34" font-weight="700">Product search</text>
83
+ <text x="524" y="338" class="m" font-size="12.5" fill="#7E8487">├─</text><path d="M552 334l3 3.2 5.6-6.4" fill="none" stroke="#2F9E44" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/><text x="568" y="338" class="m" font-size="12.5" fill="#212E34">P1 Index</text>
84
+ <text x="928" y="338" class="m" font-size="12.5" fill="#2F9E44" font-weight="600" text-anchor="end">2/2</text>
85
+ <text x="524" y="360" class="m" font-size="12.5" fill="#7E8487">├─</text><text x="552" y="360" class="m" font-size="12.5" fill="#212E34">▸ P2 Search box</text>
86
+ <text x="928" y="360" class="m k18" font-size="12.5" fill="#4A4E51" text-anchor="end" opacity="0">0/4</text><text x="928" y="360" class="m k19" font-size="12.5" fill="#212E34" font-weight="700" text-anchor="end" opacity="0">1/4</text><text x="928" y="360" class="m k20" font-size="12.5" fill="#03777D" font-weight="700" text-anchor="end">2/4</text>
87
+ <text x="524" y="380" class="m" font-size="12.5" fill="#7E8487">│ └─</text><text x="574" y="380" class="m" font-size="12" fill="#212E34" font-weight="600"><tspan fill="#03777D">▶</tspan> wire the box to the index</text>
88
+ <text x="524" y="402" class="m" font-size="12.5" fill="#7E8487">└─</text><text x="552" y="402" class="m" font-size="12.5" fill="#212E34">▸ P3 Docs</text>
89
+ <text x="928" y="402" class="m" font-size="12.5" fill="#4A4E51" text-anchor="end">0/1</text>
86
90
  </g>
87
- <path d="M498 320h430" stroke="#DDE2E4" stroke-width="1"/>
91
+ <path d="M498 420h430" stroke="#DDE2E4" stroke-width="1"/>
88
92
  <g class="k3">
89
- <text x="498" y="352" class="m" font-size="11" fill="#4A4E51">▾</text><text x="528" y="352" class="m" font-size="13" fill="#212E34" font-weight="700">checkout-fee</text><text x="640" y="352" class="m" font-size="12.5" fill="#03777D" font-weight="600">done</text><path d="M709.5 345.5h13a6.5 5.5 0 0 1-13 0Z" fill="#4A4E51"/><path d="M712 352h8" stroke="#4A4E51" stroke-width="1.6" stroke-linecap="round"/><text x="760" y="352" class="m" font-size="12.5" fill="#4A4E51" text-anchor="end">48 %</text><path d="M772 340v15" stroke="#C4CBCE" stroke-width="1.2"/><circle cx="880" cy="347.5" r="5.2" fill="none" stroke="#4A4E51" stroke-width="1.4"/><path d="M880 342.3A5.2 5.2 0 0 0 880 352.7Z" fill="#4A4E51"/>
90
- <circle cx="513" cy="347.5" r="5.4" fill="none" stroke="#03B5BD" stroke-width="1.6"/><circle cx="513" cy="347.5" r="2.6" fill="#03B5BD"/>
91
- <rect x="786" y="342" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="794" y="342" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="802" y="342" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="810" y="342" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="818" y="342" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="826" y="342" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="834" y="342" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="842" y="342" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="850" y="342" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="858" y="342" width="6.5" height="11" rx="1.2" fill="#03B5BD"/>
92
- <text x="928" y="352" class="m" font-size="12.5" fill="#4A4E51" text-anchor="end">6/6</text>
93
- <text x="528" y="374" class="m" font-size="12" fill="#212E34">↗#42 <tspan fill="#2F9E44">✓ checks</tspan> Delivery fee</text>
94
- <path d="M529 388a6 6 0 0 0 8 8z" fill="#4A4E51"/><path d="M533 392l3.5-3.5" stroke="#4A4E51" stroke-width="1.5" stroke-linecap="round"/><path d="M535 385a5 5 0 0 1 5 5" fill="none" stroke="#4A4E51" stroke-width="1.5" stroke-linecap="round"/><text x="548" y="396" class="m" font-size="12" fill="#4A4E51">PROJ-123 · In review</text>
95
- <text x="498" y="420" class="m" font-size="12.5" fill="#7E8487">└─</text><text x="524" y="420" class="s" font-size="13" fill="#212E34" font-weight="700">Checkout</text>
96
- <text x="524" y="442" class="m" font-size="12.5" fill="#7E8487">└─</text><path d="M552 438l3 3.2 5.6-6.4" fill="none" stroke="#2F9E44" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/><text x="568" y="442" class="m" font-size="12.5" fill="#212E34">P1 Fee</text>
97
- <text x="928" y="442" class="m" font-size="12.5" fill="#2F9E44" font-weight="600" text-anchor="end">6/6</text>
98
- <path d="M498 460h430" stroke="#DDE2E4" stroke-width="1"/>
99
- <text x="498" y="494" class="m" font-size="11" fill="#4A4E51">▾</text><text x="528" y="494" class="m" font-size="13" fill="#212E34" font-weight="700">docs-site</text><text x="640" y="494" class="m" font-size="12.5" fill="#4A4E51" font-weight="600">idle</text><circle cx="880" cy="489.5" r="5.2" fill="none" stroke="#4A4E51" stroke-width="1.4"/><path d="M880 484.3A5.2 5.2 0 0 0 880 494.7Z" fill="#4A4E51"/>
100
- <circle cx="513" cy="489.5" r="5" fill="none" stroke="#4A4E51" stroke-width="1.6"/>
101
- <rect x="786" y="484" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="794" y="484" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="802" y="484" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="810" y="484" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="818" y="484" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="826" y="484" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="834" y="484" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="842" y="484" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="850" y="484" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="858" y="484" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/>
102
- <text x="928" y="494" class="m" font-size="12.5" fill="#4A4E51" text-anchor="end">0/3</text>
103
- <text x="528" y="516" class="m" font-size="12" fill="#4A4E51">starts after search</text>
104
- <path d="M498 534h430" stroke="#DDE2E4" stroke-width="1"/>
93
+ <text x="498" y="452" class="m" font-size="11" fill="#4A4E51">▾</text><text x="528" y="452" class="m" font-size="13" fill="#212E34" font-weight="700">checkout-fee</text><text x="640" y="452" class="m" font-size="12.5" fill="#03777D" font-weight="600">done</text><path d="M709.5 445.5h13a6.5 5.5 0 0 1-13 0Z" fill="#4A4E51"/><path d="M712 452h8" stroke="#4A4E51" stroke-width="1.6" stroke-linecap="round"/><text x="760" y="452" class="m" font-size="12.5" fill="#4A4E51" text-anchor="end">48 %</text><path d="M772 440v15" stroke="#C4CBCE" stroke-width="1.2"/><circle cx="880" cy="447.5" r="5.2" fill="none" stroke="#4A4E51" stroke-width="1.4"/><path d="M880 442.3A5.2 5.2 0 0 0 880 452.7Z" fill="#4A4E51"/>
94
+ <circle cx="513" cy="447.5" r="5.4" fill="none" stroke="#03B5BD" stroke-width="1.6"/><circle cx="513" cy="447.5" r="2.6" fill="#03B5BD"/>
95
+ <rect x="786" y="442" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="794" y="442" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="802" y="442" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="810" y="442" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="818" y="442" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="826" y="442" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="834" y="442" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="842" y="442" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="850" y="442" width="6.5" height="11" rx="1.2" fill="#03B5BD"/><rect x="858" y="442" width="6.5" height="11" rx="1.2" fill="#03B5BD"/>
96
+ <text x="928" y="452" class="m" font-size="12.5" fill="#4A4E51" text-anchor="end">6/6</text>
97
+ <text x="528" y="474" class="m" font-size="12" fill="#212E34">↗#42 <tspan fill="#2F9E44">✓ checks</tspan> Delivery fee</text>
98
+ <path d="M529 488a6 6 0 0 0 8 8z" fill="#4A4E51"/><path d="M533 492l3.5-3.5" stroke="#4A4E51" stroke-width="1.5" stroke-linecap="round"/><path d="M535 485a5 5 0 0 1 5 5" fill="none" stroke="#4A4E51" stroke-width="1.5" stroke-linecap="round"/><text x="548" y="496" class="m" font-size="12" fill="#4A4E51">PROJ-123 · In review</text>
99
+ <text x="498" y="520" class="m" font-size="12.5" fill="#7E8487">└─</text><text x="524" y="520" class="s" font-size="13" fill="#212E34" font-weight="700">Checkout</text>
100
+ <text x="524" y="542" class="m" font-size="12.5" fill="#7E8487">└─</text><path d="M552 538l3 3.2 5.6-6.4" fill="none" stroke="#2F9E44" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/><text x="568" y="542" class="m" font-size="12.5" fill="#212E34">P1 Fee</text>
101
+ <text x="928" y="542" class="m" font-size="12.5" fill="#2F9E44" font-weight="600" text-anchor="end">6/6</text>
102
+ <path d="M498 560h430" stroke="#DDE2E4" stroke-width="1"/>
103
+ <text x="498" y="594" class="m" font-size="11" fill="#4A4E51">▾</text><text x="528" y="594" class="m" font-size="13" fill="#212E34" font-weight="700">docs-site</text><text x="640" y="594" class="m" font-size="12.5" fill="#4A4E51" font-weight="600">idle</text><circle cx="880" cy="589.5" r="5.2" fill="none" stroke="#4A4E51" stroke-width="1.4"/><path d="M880 584.3A5.2 5.2 0 0 0 880 594.7Z" fill="#4A4E51"/>
104
+ <circle cx="513" cy="589.5" r="5" fill="none" stroke="#4A4E51" stroke-width="1.6"/>
105
+ <rect x="786" y="584" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="794" y="584" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="802" y="584" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="810" y="584" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="818" y="584" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="826" y="584" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="834" y="584" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="842" y="584" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="850" y="584" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/><rect x="858" y="584" width="6.5" height="11" rx="1.2" fill="#C4CBCE"/>
106
+ <text x="928" y="594" class="m" font-size="12.5" fill="#4A4E51" text-anchor="end">0/3</text>
107
+ <text x="528" y="616" class="m" font-size="12" fill="#4A4E51">starts after search</text>
108
+ <path d="M498 634h430" stroke="#DDE2E4" stroke-width="1"/>
105
109
  </g>
106
- <text x="498" y="604" class="m" font-size="11.5" fill="#4A4E51">↑↓ select →← open a show ticked s html q quit ? help</text>
107
- <rect x="4" y="641" width="952" height="60" rx="18" fill="#FAF9F8" stroke="#C4CBCE" stroke-width="1.5"/>
108
- <rect class="k21" opacity="0" x="26" y="650" width="165" height="42" rx="21" fill="#03B5BD" fill-opacity="0.16" stroke="#03B5BD" stroke-width="1.5"/>
110
+ <text x="498" y="704" class="m" font-size="11.5" fill="#4A4E51">↑↓ select →← open a show ticked s html q quit ? help</text>
111
+ <rect x="4" y="741" width="952" height="60" rx="18" fill="#FAF9F8" stroke="#C4CBCE" stroke-width="1.5"/>
112
+ <rect class="k21" opacity="0" x="26" y="750" width="165" height="42" rx="21" fill="#03B5BD" fill-opacity="0.16" stroke="#03B5BD" stroke-width="1.5"/>
109
113
  <g class="k22">
110
- <circle cx="47" cy="671" r="11" fill="#212E34"/>
111
- <text x="47" y="675.5" class="s" font-size="12.5" fill="#03B5BD" font-weight="800" text-anchor="middle">1</text>
112
- <text x="65" y="675.5" class="s" font-size="12.5" fill="#212E34" font-weight="600">Lane search works</text>
114
+ <circle cx="47" cy="771" r="11" fill="#212E34"/>
115
+ <text x="47" y="775.5" class="s" font-size="12.5" fill="#03B5BD" font-weight="800" text-anchor="middle">1</text>
116
+ <text x="65" y="775.5" class="s" font-size="12.5" fill="#212E34" font-weight="600">Lane search works</text>
113
117
  </g>
114
- <rect class="k23" opacity="0" x="203" y="650" width="214" height="42" rx="21" fill="#03B5BD" fill-opacity="0.16" stroke="#03B5BD" stroke-width="1.5"/>
118
+ <rect class="k23" opacity="0" x="203" y="750" width="214" height="42" rx="21" fill="#03B5BD" fill-opacity="0.16" stroke="#03B5BD" stroke-width="1.5"/>
115
119
  <g class="k24">
116
- <circle cx="224" cy="671" r="11" fill="#212E34"/>
117
- <text x="224" y="675.5" class="s" font-size="12.5" fill="#03B5BD" font-weight="800" text-anchor="middle">2</text>
118
- <text x="242" y="675.5" class="s" font-size="12.5" fill="#212E34" font-weight="600">It hits an open question</text>
120
+ <circle cx="224" cy="771" r="11" fill="#212E34"/>
121
+ <text x="224" y="775.5" class="s" font-size="12.5" fill="#03B5BD" font-weight="800" text-anchor="middle">2</text>
122
+ <text x="242" y="775.5" class="s" font-size="12.5" fill="#212E34" font-weight="600">It hits an open question</text>
119
123
  </g>
120
- <rect class="k25" opacity="0" x="429" y="650" width="214" height="42" rx="21" fill="#03B5BD" fill-opacity="0.16" stroke="#03B5BD" stroke-width="1.5"/>
124
+ <rect class="k25" opacity="0" x="429" y="750" width="214" height="42" rx="21" fill="#03B5BD" fill-opacity="0.16" stroke="#03B5BD" stroke-width="1.5"/>
121
125
  <g class="k26">
122
- <circle cx="450" cy="671" r="11" fill="#212E34"/>
123
- <text x="450" y="675.5" class="s" font-size="12.5" fill="#03B5BD" font-weight="800" text-anchor="middle">3</text>
124
- <text x="468" y="675.5" class="s" font-size="12.5" fill="#212E34" font-weight="600">The coordinator asks you</text>
126
+ <circle cx="450" cy="771" r="11" fill="#212E34"/>
127
+ <text x="450" y="775.5" class="s" font-size="12.5" fill="#03B5BD" font-weight="800" text-anchor="middle">3</text>
128
+ <text x="468" y="775.5" class="s" font-size="12.5" fill="#212E34" font-weight="600">The coordinator asks you</text>
125
129
  </g>
126
- <rect class="k27" opacity="0" x="655" y="650" width="102" height="42" rx="21" fill="#03B5BD" fill-opacity="0.16" stroke="#03B5BD" stroke-width="1.5"/>
130
+ <rect class="k27" opacity="0" x="655" y="750" width="102" height="42" rx="21" fill="#03B5BD" fill-opacity="0.16" stroke="#03B5BD" stroke-width="1.5"/>
127
131
  <g class="k28">
128
- <circle cx="676" cy="671" r="11" fill="#212E34"/>
129
- <text x="676" y="675.5" class="s" font-size="12.5" fill="#03B5BD" font-weight="800" text-anchor="middle">4</text>
130
- <text x="694" y="675.5" class="s" font-size="12.5" fill="#212E34" font-weight="600">You pick</text>
132
+ <circle cx="676" cy="771" r="11" fill="#212E34"/>
133
+ <text x="676" y="775.5" class="s" font-size="12.5" fill="#03B5BD" font-weight="800" text-anchor="middle">4</text>
134
+ <text x="694" y="775.5" class="s" font-size="12.5" fill="#212E34" font-weight="600">You pick</text>
131
135
  </g>
132
- <rect class="k29" opacity="0" x="769" y="650" width="165" height="42" rx="21" fill="#03B5BD" fill-opacity="0.16" stroke="#03B5BD" stroke-width="1.5"/>
136
+ <rect class="k29" opacity="0" x="769" y="750" width="165" height="42" rx="21" fill="#03B5BD" fill-opacity="0.16" stroke="#03B5BD" stroke-width="1.5"/>
133
137
  <g class="k30">
134
- <circle cx="790" cy="671" r="11" fill="#212E34"/>
135
- <text x="790" y="675.5" class="s" font-size="12.5" fill="#03B5BD" font-weight="800" text-anchor="middle">5</text>
136
- <text x="808" y="675.5" class="s" font-size="12.5" fill="#212E34" font-weight="600">The lane works on</text>
138
+ <circle cx="790" cy="771" r="11" fill="#212E34"/>
139
+ <text x="790" y="775.5" class="s" font-size="12.5" fill="#03B5BD" font-weight="800" text-anchor="middle">5</text>
140
+ <text x="808" y="775.5" class="s" font-size="12.5" fill="#212E34" font-weight="600">The lane works on</text>
137
141
  </g>
138
142
  </svg>
@@ -1,6 +1,6 @@
1
1
  id = "crossdelta.jackherd"
2
2
  name = "jackherd"
3
- version = "1.9.0"
3
+ version = "1.10.0"
4
4
  min_herdr_version = "0.8.2"
5
5
  description = "Show the herd, the state of a board's lanes, in a herdr pane"
6
6
  platforms = ["linux", "macos"]
package/install.sh CHANGED
@@ -9,7 +9,7 @@ set -eu
9
9
 
10
10
  # The default is the version this file ships with. The release bump changes it together with
11
11
  # package.json; install.test.ts fails while the two differ. Overridable, but only by a plain release.
12
- JACKHERD_VERSION="${JACKHERD_VERSION:-1.9.0}"
12
+ JACKHERD_VERSION="${JACKHERD_VERSION:-1.10.0}"
13
13
 
14
14
  JACKHERD_PACKAGE='jackherd'
15
15
  # Test seam: the terminal the wizard asks its questions on.
@@ -0,0 +1,15 @@
1
+ [
2
+ "Bash(gh run rerun *)",
3
+ "Bash(gh api *pulls/*/merge*)",
4
+ "Bash(gh api *merges*)",
5
+ "Bash(gh api *contents/*)",
6
+ "Bash(gh api *dispatches*)",
7
+ "Bash(gh api *rerun*)",
8
+ "Bash(gh api *git/refs*)",
9
+ "Bash(jh lane close*)",
10
+ "Bash(jackherd lane close*)",
11
+ "Bash(bunx jackherd* lane close*)",
12
+ "Bash(bun */cli.ts lane close*)",
13
+ "Bash(bunx jh lane close*)",
14
+ "Bash(bun x jackherd* lane close*)"
15
+ ]
@@ -1,28 +1,96 @@
1
1
  import { ASK_USER_QUESTION_TOOL } from '../shared/dialog-answers'
2
2
 
3
+ export { isLanePrompt } from '../shared/transcript'
4
+
5
+ export type TurnGate = {
6
+ message: string
7
+ stopHookActive: boolean
8
+ enabled: boolean
9
+ }
10
+
3
11
  export type HintInput = {
4
12
  message: string
5
13
  tools: string[]
6
14
  lane: boolean
7
- stopHookActive: boolean
8
- enabled: boolean
9
15
  }
10
16
 
11
- export type HintDecision = { kind: 'silent' } | { kind: 'hint'; message: string }
17
+ export type HintDecision = { kind: 'silent' } | { kind: 'block'; reason: string }
18
+
19
+
20
+ const FENCE_OPENING = /^\s*(`{3,}|~{3,})/
21
+ const FENCE_CLOSING = /^\s*(`{3,}|~{3,})\s*$/
22
+ const QUOTE = /^\s*>/
23
+ const HEADING = /^\s{0,3}#{1,6}\s/
24
+ const LIST_ITEM = /^\s*([-*+]|\d+[.)])\s/
25
+ const INDENTED = /^( {4,}|\t)/
26
+ const URL_AT_END = /\S+:\/\/\S*\?$/
27
+ const QUESTION_LINE = /[??][\s*_"')\]“”’»«]*$/
28
+ const REASON_QUESTION_LENGTH = 120
29
+
30
+ const shorten = (line: string): string =>
31
+ line.length > REASON_QUESTION_LENGTH ? `${line.slice(0, REASON_QUESTION_LENGTH - 1)}…` : line
32
+
33
+ const closesFence = (line: string, opening: string): boolean => {
34
+ const closing = FENCE_CLOSING.exec(line)?.[1]
35
+ return closing !== undefined && closing[0] === opening[0] && closing.length >= opening.length
36
+ }
37
+
38
+ /**
39
+ * The lines outside code fences. A fence closes on the same character with at least the opening length, so a
40
+ * three-backtick line inside a `~~~` block or a four-backtick block stays code. An unclosed fence hides the rest, as a
41
+ * Markdown renderer shows it.
42
+ */
43
+ const proseLines = (message: string): string[] => {
44
+ let opening: string | undefined
45
+ return message.split('\n').filter((line) => {
46
+ if (opening !== undefined) {
47
+ if (closesFence(line, opening)) opening = undefined
48
+ return false
49
+ }
50
+ opening = FENCE_OPENING.exec(line)?.[1]
51
+ return opening === undefined
52
+ })
53
+ }
54
+
55
+ /** Drops indented code lines; an indented continuation of a list item is prose and stays. */
56
+ const withoutIndentedCode = (lines: string[]): string[] => {
57
+ let inList = false
58
+ return lines.filter((line) => {
59
+ if (line.trim() === '') return true
60
+ if (LIST_ITEM.test(line)) {
61
+ inList = true
62
+ return true
63
+ }
64
+ if (!INDENTED.test(line)) {
65
+ inList = false
66
+ return true
67
+ }
68
+ return inList
69
+ })
70
+ }
12
71
 
13
- /** A lane prompt names the lane in its first line; a lane started from a hand-over file reads the file first. */
14
- const LANE_PROMPT = /^\s*(<pasted_content[^>]*>\s*)?(Du bist die Spur|You are the lane|Read `\.claude\/handover\/)/i
72
+ const isNoQuestionLine = (line: string): boolean => QUOTE.test(line) || HEADING.test(line) || URL_AT_END.test(line)
15
73
 
16
- const HINT =
17
- 'Claude\'s last reply ends in a question, but no AskUserQuestion dialog was used. For choices with fixed answers, ask Claude to use the dialog. Turn this notice off with {"hints": {"askUserQuestion": false}} in .jackherd/config.json.'
74
+ /**
75
+ * The first line of the message that asks the user something: it ends in a `?` (closing emphasis, quotes and brackets
76
+ * after it do not hide it) and sits outside code fences, indented code, quotes and headings. A `?` inside a line, at
77
+ * the end of a URL or in inline code at the line's end is no question. A bold line (`**Why?**`) is a question.
78
+ */
79
+ export const findQuestion = (message: string): string | undefined =>
80
+ withoutIndentedCode(proseLines(message))
81
+ .filter((line) => !isNoQuestionLine(line))
82
+ .map((line) => line.trim())
83
+ .find((line) => QUESTION_LINE.test(line))
18
84
 
19
- /** The last character that counts is a `?`; closing emphasis, quotes and brackets after it do not hide it. */
20
- export const endsWithQuestion = (message: string): boolean => /[??][\s*_"')\]“”’»«]*$/.test(message)
85
+ const reasonFor = (question: string): string => `Ask the question with AskUserQuestion: "${shorten(question)}"`
21
86
 
22
- export const isLanePrompt = (firstPrompt: string): boolean => LANE_PROMPT.test(firstPrompt)
87
+ /** Whether the transcript is worth reading: the hint is on, this is no follow-up stop, and the message asks something. */
88
+ export const shouldReadTurn = ({ message, stopHookActive, enabled }: TurnGate): boolean =>
89
+ enabled && !stopHookActive && findQuestion(message) !== undefined
23
90
 
24
- export const decideHint = (input: HintInput): HintDecision => {
25
- if (!input.enabled || input.stopHookActive || input.lane) return { kind: 'silent' }
26
- if (!endsWithQuestion(input.message) || input.tools.includes(ASK_USER_QUESTION_TOOL)) return { kind: 'silent' }
27
- return { kind: 'hint', message: HINT }
91
+ /** What is left to decide once the transcript is read; `shouldReadTurn` has already passed. */
92
+ export const decideHint = ({ message, tools, lane }: HintInput): HintDecision => {
93
+ if (lane || tools.includes(ASK_USER_QUESTION_TOOL)) return { kind: 'silent' }
94
+ const question = findQuestion(message)
95
+ return question === undefined ? { kind: 'silent' } : { kind: 'block', reason: reasonFor(question) }
28
96
  }
@@ -1,11 +1,13 @@
1
- // Stop hook, shipped by jackherd. Points out, once per turn, a last message that ends in a question while the turn used
2
- // no AskUserQuestion dialog. It only hints (a `systemMessage` for the user, exit 0) and never blocks the stop. Any failure
3
- // inside the hook is silent (a module that does not load or a missing `bun` is not caught here): a hint must not fail a turn. The Stop input carries `last_assistant_message`, `prompt_id` and
4
- // `transcript_path`; the tools of the turn and the lane prompt come from the transcript, read only when the message is a question.
1
+ // Stop hook, shipped by jackherd. Holds the end of the turn once when the last message asks the user something in a line
2
+ // outside code and quotes while the turn used no AskUserQuestion dialog (`decision: "block"` with a reason, exit 0).
3
+ // `stop_hook_active` on the follow-up stop keeps it from looping. Failures inside `hintFor` are silent; a module that
4
+ // fails to load or a missing `bun` surfaces as a hook error. The Stop input carries `last_assistant_message`,
5
+ // `prompt_id` and `transcript_path`; the tools of the turn and the lane prompt come from the transcript, read only when
6
+ // the message is a question.
5
7
  import { readFileSync } from 'node:fs'
6
- import { decideHint, endsWithQuestion, isLanePrompt } from './decision'
8
+ import { isLanePrompt, readTurn } from '../shared/transcript'
9
+ import { decideHint, shouldReadTurn } from './decision'
7
10
  import { isHintEnabled } from './settings'
8
- import { readTurn } from './transcript'
9
11
 
10
12
  type StopInput = {
11
13
  last_assistant_message?: unknown
@@ -15,19 +17,13 @@ type StopInput = {
15
17
  }
16
18
 
17
19
  const hintFor = (input: StopInput) => {
18
- if (typeof input.last_assistant_message !== 'string' || !endsWithQuestion(input.last_assistant_message)) return undefined
19
- if (typeof input.transcript_path !== 'string' || input.stop_hook_active === true) return undefined
20
- const projectDirectory = process.env.CLAUDE_PROJECT_DIR ?? process.cwd()
21
- if (!isHintEnabled(projectDirectory)) return undefined
20
+ const { last_assistant_message: message, transcript_path: transcriptPath } = input
21
+ if (typeof message !== 'string' || typeof transcriptPath !== 'string') return undefined
22
+ const enabled = isHintEnabled(process.env.CLAUDE_PROJECT_DIR ?? process.cwd())
23
+ if (!shouldReadTurn({ message, stopHookActive: input.stop_hook_active === true, enabled })) return undefined
22
24
  const promptId = typeof input.prompt_id === 'string' ? input.prompt_id : undefined
23
- const turn = readTurn(readFileSync(input.transcript_path, 'utf8'), promptId)
24
- return decideHint({
25
- message: input.last_assistant_message,
26
- tools: turn.tools,
27
- lane: isLanePrompt(turn.firstPrompt),
28
- stopHookActive: false,
29
- enabled: true,
30
- })
25
+ const turn = readTurn(readFileSync(transcriptPath, 'utf8'), promptId)
26
+ return decideHint({ message, tools: turn.tools, lane: isLanePrompt(turn.firstPrompt) })
31
27
  }
32
28
 
33
29
  const decide = async () => {
@@ -40,5 +36,5 @@ const decide = async () => {
40
36
 
41
37
  const decision = await decide()
42
38
 
43
- if (decision?.kind === 'hint') console.log(JSON.stringify({ systemMessage: decision.message }))
39
+ if (decision?.kind === 'block') console.log(JSON.stringify({ decision: 'block', reason: decision.reason }))
44
40
  process.exit(0)
@@ -1,21 +1,6 @@
1
- import { existsSync, readFileSync } from 'node:fs'
2
1
  import { homedir } from 'node:os'
3
- import { join } from 'node:path'
2
+ import { isHintOn, readConfig } from '../shared/config'
4
3
 
5
- const isRecord = (value: unknown): value is Record<string, unknown> =>
6
- typeof value === 'object' && value !== null && !Array.isArray(value)
7
-
8
- const readHintsSection = (directories: string[]): Record<string, unknown> => {
9
- const path = directories.map((directory) => join(directory, '.jackherd', 'config.json')).find((candidate) => existsSync(candidate))
10
- if (!path) return {}
11
- try {
12
- const config: unknown = JSON.parse(readFileSync(path, 'utf8'))
13
- return isRecord(config) && isRecord(config.hints) ? config.hints : {}
14
- } catch {
15
- return {}
16
- }
17
- }
18
-
19
- /** On unless the first config that exists says `hints.askUserQuestion: false`; a malformed value keeps the hint on. */
4
+ /** On unless the first config that sets it (project before home) says `hints.askUserQuestion: false`. */
20
5
  export const isHintEnabled = (projectDirectory: string, home: string = homedir()): boolean =>
21
- readHintsSection([projectDirectory, home]).askUserQuestion !== false
6
+ isHintOn([readConfig([projectDirectory]), readConfig([home])], 'askUserQuestion')