jackherd 1.8.1 → 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.
- package/CHANGELOG.md +60 -0
- package/INSTALL.md +3 -1
- package/README.md +75 -64
- package/dist/{chunk-OU2KALXP.js → chunk-5MQBCUQ5.js} +24 -4
- package/dist/config.d.ts +45 -4
- package/dist/config.js +84 -27
- package/dist/plan-format.d.ts +63 -7
- package/dist/plan-format.js +66 -12
- package/docs/workflow-dark.svg +67 -63
- package/docs/workflow.svg +67 -63
- package/herdr-plugin/herdr-plugin.toml +1 -1
- package/install.sh +1 -1
- package/kit/hooks/ask-rules.json +15 -0
- package/kit/hooks/hooks/approval-gate/index.ts +22 -4
- package/kit/hooks/hooks/approval-gate/run.ts +29 -6
- package/kit/hooks/hooks/approval-gate/subagent.ts +18 -0
- package/kit/hooks/hooks/ask-hint/decision.ts +82 -14
- package/kit/hooks/hooks/ask-hint/index.ts +15 -19
- package/kit/hooks/hooks/ask-hint/settings.ts +3 -18
- package/kit/hooks/hooks/git-guard/decision.ts +50 -102
- package/kit/hooks/hooks/merge-guard/always-ask.ts +63 -0
- package/kit/hooks/hooks/merge-guard/{plan-approval-settings.ts → board-settings.ts} +21 -67
- package/kit/hooks/hooks/merge-guard/check-free.ts +20 -0
- package/kit/hooks/hooks/merge-guard/decision.ts +127 -140
- package/kit/hooks/hooks/merge-guard/index.ts +6 -3
- package/kit/hooks/hooks/merge-guard/lane-plan.ts +79 -0
- package/kit/hooks/hooks/merge-guard/run.ts +59 -54
- package/kit/hooks/hooks/merge-guard/settings.ts +13 -1
- package/kit/hooks/hooks/merge-guard/standing-approval.ts +142 -0
- package/kit/hooks/hooks/open-question/index.ts +26 -0
- package/kit/hooks/hooks/open-question/record.ts +135 -0
- package/kit/hooks/hooks/open-question/run.ts +52 -0
- package/kit/hooks/hooks/open-question/store.ts +39 -0
- package/kit/hooks/hooks/sent-file/index.ts +19 -0
- package/kit/hooks/hooks/sent-file/record.ts +128 -0
- package/kit/hooks/hooks/sent-file/run.ts +48 -0
- package/kit/hooks/hooks/sent-file/store.ts +9 -0
- package/kit/hooks/hooks/shared/board-lanes.ts +15 -2
- package/kit/hooks/hooks/shared/config.ts +7 -0
- package/kit/hooks/hooks/shared/dialog-answers.ts +9 -9
- package/kit/hooks/hooks/shared/files.ts +7 -0
- package/kit/hooks/hooks/shared/lane.ts +1 -1
- package/kit/hooks/hooks/shared/newest-verdict.ts +10 -0
- package/kit/hooks/hooks/shared/private-directory.ts +56 -9
- package/kit/hooks/hooks/shared/program-position.ts +2 -75
- package/kit/hooks/hooks/shared/shell-aliases.ts +1 -71
- package/kit/hooks/hooks/{ask-hint → shared}/transcript.ts +37 -6
- package/kit/hooks/hooks/{merge-guard → shared}/xargs-program.ts +10 -8
- package/kit/hooks/hooks/task-hint/decision.ts +49 -0
- package/kit/hooks/hooks/task-hint/index.ts +79 -0
- package/kit/hooks/hooks/task-hint/run.ts +62 -0
- package/kit/hooks/hooks/task-hint/store.ts +51 -0
- package/kit/hooks/hooks.json +21 -4
- package/kit/hooks/statusline.sh +1 -1
- package/kit/review/agents/skill-review.md +34 -0
- package/kit/review/skills/pre-commit/SKILL.md +1 -1
- package/kit/workflow/skills/follow/SKILL.md +1 -8
- package/kit/workflow/skills/implement/SKILL.md +10 -6
- package/kit/workflow/skills/orchestrate/SKILL.md +213 -412
- package/kit/workflow/skills/orchestrate/handover/decision.ts +43 -8
- package/kit/workflow/skills/orchestrate/handover/index.ts +32 -4
- package/kit/workflow/skills/orchestrate/handover/standing-approval.ts +84 -0
- package/kit/workflow/skills/orchestrate/references/acceptance.md +15 -0
- package/kit/workflow/skills/orchestrate/references/close-lane.md +17 -0
- package/kit/workflow/skills/orchestrate/references/coordinator-handover.md +43 -17
- package/kit/workflow/skills/orchestrate/references/draft-review.md +19 -0
- package/kit/workflow/skills/orchestrate/references/events.md +86 -0
- package/kit/workflow/skills/orchestrate/references/herd.md +6 -6
- package/kit/workflow/skills/orchestrate/references/lane-handover.md +18 -0
- package/kit/workflow/skills/orchestrate/references/lane-session.md +25 -0
- package/kit/workflow/skills/orchestrate/references/lane-setup.md +37 -0
- package/kit/workflow/skills/orchestrate/references/plan-commit.md +22 -10
- package/kit/workflow/skills/orchestrate/references/sensitive-input.md +5 -7
- package/kit/workflow/skills/plan/SKILL.md +6 -5
- package/kit/workflow/skills/plan/references/template-English.md +4 -4
- package/kit/workflow/skills/plan/references/template-German.md +4 -4
- package/package.json +4 -1
- package/recipes/README.md +2 -1
- package/recipes/sources.md +1 -1
- package/schemas/jackherd.schema.json +85 -7
- package/src/check/agent-text-reviewers.ts +23 -0
- package/src/check/command.ts +2 -0
- package/src/check/plan-structure-check.ts +49 -2
- package/src/check/plan.ts +8 -1
- package/src/check/schema.ts +9 -0
- package/src/check/skill-budget-defaults.ts +55 -0
- package/src/check/skill-budgets.ts +185 -0
- package/src/cli.ts +4 -0
- package/src/config/auto-window.ts +8 -0
- package/src/config/link-kinds.ts +7 -1
- package/src/config/schema.ts +80 -26
- package/src/context/fill.ts +12 -0
- package/src/context/session-log.ts +43 -1
- package/src/lane/close-board.ts +106 -0
- package/src/lane/close-check.ts +183 -0
- package/src/lane/close-command-line.ts +11 -0
- package/src/lane/close-command.ts +217 -0
- package/src/lane/close-options.ts +43 -0
- package/src/lane/close-survey.ts +83 -0
- package/src/lane/close.ts +141 -0
- package/src/lane/command.ts +13 -0
- package/src/lane-prompt/build.ts +74 -0
- package/src/lane-prompt/command.ts +87 -0
- package/src/lane-prompt/input.ts +116 -0
- package/src/lane-prompt/options.ts +68 -0
- package/src/lane-prompt/texts.ts +101 -0
- package/src/plan-format/clock.ts +12 -0
- package/src/plan-format/dependencies.ts +9 -9
- package/src/plan-format/index.ts +1 -0
- package/src/plan-format/lanes.ts +30 -5
- package/src/plan-format/short-names.ts +71 -0
- package/src/plan-format/terminal-text.ts +4 -0
- package/src/plan-format/waiting.ts +53 -0
- package/src/plans/active-tasks.ts +60 -0
- package/src/plans/appointment-rows.ts +59 -0
- package/src/plans/ask-coordinator.ts +77 -0
- package/src/plans/ask-target.ts +25 -0
- package/src/plans/child-stop.ts +51 -0
- package/src/plans/closable-records.ts +34 -0
- package/src/plans/command.ts +3 -2
- package/src/plans/current-phase.ts +17 -7
- package/src/plans/current-task.ts +63 -5
- package/src/plans/fetch.ts +26 -3
- package/src/plans/git.ts +19 -5
- package/src/plans/herd-claim.ts +173 -0
- package/src/plans/herd-memory.ts +36 -25
- package/src/plans/lane-plans.ts +44 -13
- package/src/plans/lane-standing.ts +9 -4
- package/src/plans/lane-summary.ts +367 -92
- package/src/plans/lane-tickets.ts +15 -0
- package/src/plans/layout-phase.ts +80 -19
- package/src/plans/layout-summary.ts +163 -74
- package/src/plans/layout.ts +472 -114
- package/src/plans/link-chips.ts +1 -0
- package/src/plans/live.ts +119 -10
- package/src/plans/phase-ids.ts +8 -0
- package/src/plans/phase-models.ts +1 -1
- package/src/plans/phase-ranges.ts +45 -0
- package/src/plans/phase-source.ts +15 -0
- package/src/plans/plan-cache.ts +28 -0
- package/src/plans/plan-heads.ts +54 -0
- package/src/plans/pull-requests.ts +23 -2
- package/src/plans/question-records.ts +48 -0
- package/src/plans/questions-read.ts +13 -0
- package/src/plans/read-cache.ts +8 -0
- package/src/plans/record-id.ts +4 -0
- package/src/plans/restart-decision.ts +13 -4
- package/src/plans/selection.ts +117 -34
- package/src/plans/sent-files-read.ts +13 -0
- package/src/plans/sent-files.ts +114 -0
- package/src/plans/settings.ts +2 -1
- package/src/plans/signalled-phase.ts +42 -0
- package/src/plans/signals-read.ts +62 -0
- package/src/plans/snapshot.ts +279 -60
- package/src/plans/sprint-export.ts +11 -11
- package/src/plans/sprint-page.ts +5 -5
- package/src/plans/subagent-phases.ts +95 -0
- package/src/plans/subagent-tasks.ts +51 -0
- package/src/plans/subagents-read.ts +131 -0
- package/src/plans/subagents.ts +190 -0
- package/src/plans/supervisor.ts +14 -4
- package/src/plans/symbols.ts +3 -0
- package/src/plans/task-name.ts +34 -0
- package/src/plans/texts.ts +36 -14
- package/src/plans/unlisted-lanes.ts +10 -5
- package/src/plans/view.ts +20 -12
- package/src/plans/waiting-click.ts +231 -0
- package/src/plans/waiting-records.ts +39 -0
- package/src/plans/waiting-window.ts +8 -0
- package/src/preview/command.ts +105 -0
- package/src/preview/embed.ts +209 -0
- package/src/preview/images.ts +89 -0
- package/src/preview/open.ts +12 -0
- package/src/preview/options.ts +32 -0
- package/src/preview/page.ts +89 -0
- package/src/preview/render.ts +41 -0
- package/src/settled/board-row.ts +1 -2
- package/src/shared/is-record.ts +2 -0
- package/src/shared/private-location.ts +89 -0
- package/src/shared/private-records.ts +48 -0
- package/src/shared/stat-cache.ts +48 -0
- package/src/sync/kit-source.ts +29 -10
- package/src/sync/kit.ts +2 -0
- package/src/sync/manifest.ts +2 -0
- package/src/sync/plan-ask-rules.ts +94 -0
- package/src/sync/plan-hooks.ts +2 -0
- package/src/sync/render.ts +3 -3
- package/src/sync/settings-hooks.ts +4 -1
- package/src/sync/sync-plan.ts +1 -0
- package/src/sync/sync.ts +24 -3
- package/src/task-signals/agent.ts +15 -0
- package/src/task-signals/claude-read.ts +89 -0
- package/src/task-signals/claude-signals.ts +112 -0
- package/src/task-signals/command.ts +71 -0
- package/src/task-signals/fixtures/session.jsonl +7 -0
- package/src/task-signals/matcher.ts +96 -0
- package/src/task-signals/record-store.ts +40 -0
- package/src/task-signals/record.ts +68 -0
- package/src/task-signals/text.ts +13 -0
- package/src/task-signals/types.ts +14 -0
- package/src/terminal/open.ts +13 -6
- package/src/terminal/text.ts +16 -4
- package/src/terminal/width.ts +2 -1
- package/src/version/skill-diff.ts +46 -0
- package/src/vocabulary/index.ts +44 -6
- package/src/waiting/command.ts +50 -0
- package/src/watch/appointments.ts +2 -4
- package/src/watch/approvals.ts +2 -2
- package/src/watch/asking-lanes.ts +40 -0
- package/src/watch/base-tip.ts +11 -0
- package/src/watch/command.ts +401 -90
- package/src/watch/failed-tests.ts +56 -10
- package/src/watch/failure-read.ts +18 -6
- package/src/watch/failures.ts +29 -5
- package/src/watch/feedback.ts +1 -1
- package/src/watch/gh-process.ts +9 -3
- package/src/watch/heartbeat-progress.ts +6 -18
- package/src/watch/json-guards.ts +0 -3
- package/src/watch/kit-update.ts +28 -9
- package/src/watch/lane-fill.ts +1 -1
- package/src/watch/lane-model.ts +1 -1
- package/src/watch/lane-name.ts +6 -0
- package/src/watch/lane-parallel.ts +1 -1
- package/src/watch/lane-progress.ts +58 -14
- package/src/watch/lane-report-file.ts +20 -5
- package/src/watch/lane-report.ts +37 -5
- package/src/watch/lane-rest.ts +28 -0
- package/src/watch/lane-sources.ts +31 -3
- package/src/watch/main-checkout.ts +34 -0
- package/src/watch/plan-badges-read.ts +13 -7
- package/src/watch/plan-badges.ts +9 -2
- package/src/watch/previews.ts +1 -1
- package/src/watch/pull-requests.ts +8 -3
- package/src/watch/reference-paths.ts +87 -0
- package/src/watch/running-subagents.ts +43 -0
- package/src/watch/skill-change.ts +124 -32
- package/src/watch/source-change.ts +48 -0
- package/src/watch/source-path.ts +1 -1
- package/src/watch/sources.ts +7 -4
- package/src/watch/stream-output.ts +14 -8
- package/src/watch/task-parallel-auto.ts +208 -0
- package/src/watch/task-parallel-data-paths.ts +46 -0
- package/src/watch/task-parallel-input.ts +108 -0
- package/src/watch/task-parallel-read.ts +151 -0
- package/src/watch/task-parallel-touched.ts +39 -0
- package/src/watch/task-parallel.ts +235 -0
- package/src/watch/task-signal-missing.ts +69 -0
- package/src/watch/task-signal-read.ts +29 -0
- package/src/watch/texts.ts +10 -0
- package/src/watch/update-available.ts +6 -3
- package/src/watch/waiting-line.ts +19 -0
- package/src/watch/watch-events.ts +36 -1
- package/src/watch/watcher-lock.ts +25 -29
- package/src/watch/workflow-runs.ts +2 -1
- package/kit/hooks/hooks/merge-guard/plan-approval-record.ts +0 -23
- package/kit/hooks/hooks/merge-guard/plan-approval-sources.ts +0 -196
- package/kit/hooks/hooks/merge-guard/plan-approval.ts +0 -168
- package/kit/hooks/hooks/merge-guard/plan-growth.ts +0 -40
- package/kit/hooks/hooks/merge-guard/plan-pointers.ts +0 -194
- package/kit/hooks/hooks/shared/command-text-runners.ts +0 -71
- 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="
|
|
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="
|
|
10
|
-
<rect x="218" y="58" width="252" height="
|
|
11
|
-
<rect x="482" y="58" width="462" height="
|
|
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="
|
|
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="
|
|
69
|
-
<circle class="k12" cx="513" cy="
|
|
70
|
-
<path class="k4" opacity="0" d="M513
|
|
71
|
-
<text x="640" y="
|
|
72
|
-
<text x="640" y="
|
|
73
|
-
<rect x="786" y="
|
|
74
|
-
<rect class="k13" x="810" y="
|
|
75
|
-
<rect class="k14" x="818" y="
|
|
76
|
-
<text x="928" y="
|
|
77
|
-
<text x="528" y="
|
|
78
|
-
<text x="498" y="
|
|
79
|
-
<text x="524" y="
|
|
80
|
-
<text x="928" y="
|
|
81
|
-
<text x="524" y="
|
|
82
|
-
<text x="928" y="
|
|
83
|
-
<text x="524" y="
|
|
84
|
-
<text x="524" y="
|
|
85
|
-
<text x="928" y="
|
|
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
|
|
91
|
+
<path d="M498 420h430" stroke="#DDE2E4" stroke-width="1"/>
|
|
88
92
|
<g class="k3">
|
|
89
|
-
<text x="498" y="
|
|
90
|
-
<circle cx="513" cy="
|
|
91
|
-
<rect x="786" y="
|
|
92
|
-
<text x="928" y="
|
|
93
|
-
<text x="528" y="
|
|
94
|
-
<path d="M529
|
|
95
|
-
<text x="498" y="
|
|
96
|
-
<text x="524" y="
|
|
97
|
-
<text x="928" y="
|
|
98
|
-
<path d="M498
|
|
99
|
-
<text x="498" y="
|
|
100
|
-
<circle cx="513" cy="
|
|
101
|
-
<rect x="786" y="
|
|
102
|
-
<text x="928" y="
|
|
103
|
-
<text x="528" y="
|
|
104
|
-
<path d="M498
|
|
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="
|
|
107
|
-
<rect x="4" y="
|
|
108
|
-
<rect class="k21" opacity="0" x="26" y="
|
|
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="
|
|
111
|
-
<text x="47" y="
|
|
112
|
-
<text x="65" y="
|
|
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="
|
|
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="
|
|
117
|
-
<text x="224" y="
|
|
118
|
-
<text x="242" y="
|
|
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="
|
|
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="
|
|
123
|
-
<text x="450" y="
|
|
124
|
-
<text x="468" y="
|
|
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="
|
|
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="
|
|
129
|
-
<text x="676" y="
|
|
130
|
-
<text x="694" y="
|
|
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="
|
|
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="
|
|
135
|
-
<text x="790" y="
|
|
136
|
-
<text x="808" y="
|
|
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>
|
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.
|
|
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,14 +1,17 @@
|
|
|
1
1
|
// PermissionRequest hook of Claude Code, shipped by jackherd: the client adapter of the approval gate. In a lane, known
|
|
2
2
|
// by the herdr agent of the pane and not by the directory of the call, it holds a call that would open a permission
|
|
3
|
-
// dialog, records it for the coordinator and answers with the user's decision from the coordinator's dialog;
|
|
4
|
-
// and on any failure, it answers nothing and the dialog stays. argv[2..5]: the allow label, the deny label, the text for
|
|
3
|
+
// dialog, records it for the coordinator and answers with the user's decision from the coordinator's dialog; the same for
|
|
4
|
+
// a subagent of the coordinator, under its own name; elsewhere, and on any failure, it answers nothing and the dialog stays. argv[2..5]: the allow label, the deny label, the text for
|
|
5
5
|
// a call still pending after the wait, the text for a denied call, rendered into the hook entry by `sync`.
|
|
6
|
+
import { existsSync } from 'node:fs'
|
|
6
7
|
import { homedir } from 'node:os'
|
|
8
|
+
import { join } from 'node:path'
|
|
7
9
|
import { mainCheckoutOf } from '../shared/checkout'
|
|
8
10
|
import { commandOutput } from '../shared/command'
|
|
9
11
|
import { readConfig } from '../shared/config'
|
|
10
12
|
import { isRecord } from '../shared/json'
|
|
11
13
|
import { ownPaneAgent } from '../shared/own-pane'
|
|
14
|
+
import { gateSettingsOf } from './settings'
|
|
12
15
|
import { coordinatorSessions } from './herdr-panes'
|
|
13
16
|
import { type ClientCall, type GateDependencies, type GateOutcome, runGate } from './run'
|
|
14
17
|
import { approvalsDirectory, claimRequest, pruneRequests, readRequests, writeRequest } from './store'
|
|
@@ -23,17 +26,32 @@ const herdrPanes = (): unknown => {
|
|
|
23
26
|
return answer.result
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
/**
|
|
29
|
+
/** The subagent a call comes from: Claude Code adds `agent_id` and `agent_type` only to the hooks of a subagent. */
|
|
30
|
+
const subagentOf = (input: Record<string, unknown>): ClientCall['subagent'] =>
|
|
31
|
+
typeof input.agent_id === 'string'
|
|
32
|
+
? { id: input.agent_id, type: typeof input.agent_type === 'string' ? input.agent_type : null }
|
|
33
|
+
: undefined
|
|
34
|
+
|
|
35
|
+
/** Claude Code's hook input: `cwd`, `session_id`, `tool_name`, `tool_input`, and `agent_id` inside a subagent. */
|
|
27
36
|
const clientCallOf = (text: string): ClientCall | null => {
|
|
28
37
|
const input: unknown = JSON.parse(text)
|
|
29
38
|
if (!isRecord(input) || typeof input.cwd !== 'string' || typeof input.tool_name !== 'string') return null
|
|
30
39
|
if (typeof input.session_id !== 'string') return null
|
|
31
|
-
|
|
40
|
+
const subagent = subagentOf(input)
|
|
41
|
+
return {
|
|
42
|
+
cwd: input.cwd,
|
|
43
|
+
session: input.session_id,
|
|
44
|
+
tool: input.tool_name,
|
|
45
|
+
input: input.tool_input ?? null,
|
|
46
|
+
...(subagent ? { subagent } : {}),
|
|
47
|
+
}
|
|
32
48
|
}
|
|
33
49
|
|
|
34
50
|
const dependencies: GateDependencies = {
|
|
35
51
|
paneAgent: () => ownPaneAgent(),
|
|
36
52
|
mainCheckoutOf,
|
|
53
|
+
isLaneName: (main, name) =>
|
|
54
|
+
existsSync(join(main, gateSettingsOf(readConfig([main, homedir()])).worktreeDirectory, name)),
|
|
37
55
|
config: (main) => readConfig([main, homedir()]),
|
|
38
56
|
readRequests: (main) => readRequests(approvalsDirectory(main)),
|
|
39
57
|
writeRequest: (main, request) => writeRequest(approvalsDirectory(main), request),
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import type { DialogAnswer } from '../shared/dialog-answer'
|
|
2
2
|
import { ASK_USER_QUESTION_TOOL } from '../shared/dialog-answers'
|
|
3
3
|
import { isFresh } from '../shared/fresh'
|
|
4
|
-
import { laneOfAgent } from '../shared/lane'
|
|
4
|
+
import { isCoordinatorAgent, laneOfAgent } from '../shared/lane'
|
|
5
5
|
import type { PaneAgent } from '../shared/pane-agent'
|
|
6
6
|
import { gateDecision, type GateLabels } from './decision'
|
|
7
7
|
import { type ApprovalRequest, callHash, type GateCall, isStorableInput, newRequest, requestId } from './request'
|
|
8
8
|
import { gateSettingsOf } from './settings'
|
|
9
|
+
import { type SubagentCall, subagentLabel } from './subagent'
|
|
9
10
|
|
|
10
|
-
/** A call as the client's hook hands it over, before it is known to be a lane's. */
|
|
11
|
-
export type ClientCall = { cwd: string; session: string; tool: string; input: unknown }
|
|
11
|
+
/** A call as the client's hook hands it over, before it is known to be a lane's; `subagent` when a subagent makes it. */
|
|
12
|
+
export type ClientCall = { cwd: string; session: string; tool: string; input: unknown; subagent?: SubagentCall }
|
|
12
13
|
|
|
13
14
|
/** What the gate decided; null decides nothing, so the client's own dialog stays. */
|
|
14
15
|
export type GateOutcome = { kind: 'allow' } | { kind: 'denied' } | { kind: 'pending' } | null
|
|
@@ -18,6 +19,8 @@ export type GateDependencies = {
|
|
|
18
19
|
/** The agent of the multiplexer pane this session runs in; null outside the multiplexer. */
|
|
19
20
|
paneAgent: () => PaneAgent | null
|
|
20
21
|
mainCheckoutOf: (cwd: string) => string
|
|
22
|
+
/** Whether a worktree of this name exists below the main checkout's worktree directory. */
|
|
23
|
+
isLaneName: (main: string, name: string) => boolean
|
|
21
24
|
config: (main: string) => Record<string, unknown>
|
|
22
25
|
readRequests: (main: string) => ApprovalRequest[]
|
|
23
26
|
writeRequest: (main: string, request: ApprovalRequest) => void
|
|
@@ -75,11 +78,31 @@ const awaitDecision = async (
|
|
|
75
78
|
/** A question to the user asks for an answer, not for a permission: the lane asks the coordinator in its text instead. */
|
|
76
79
|
const isHeld = ({ tool, input }: ClientCall): boolean => tool !== ASK_USER_QUESTION_TOOL && isStorableInput(input)
|
|
77
80
|
|
|
81
|
+
/**
|
|
82
|
+
* What holds the call: the lane of the pane's agent, else, for a subagent of the coordinator, its own name. The
|
|
83
|
+
* coordinator's session is the one the hook was called for, and the agent of the pane started in the main checkout
|
|
84
|
+
* under no lane's name; the subagent's permission dialog would open in that pane.
|
|
85
|
+
*/
|
|
86
|
+
const holderOf = (
|
|
87
|
+
clientCall: ClientCall,
|
|
88
|
+
agent: PaneAgent,
|
|
89
|
+
main: string,
|
|
90
|
+
worktreeDirectory: string,
|
|
91
|
+
dependencies: GateDependencies,
|
|
92
|
+
): string | null => {
|
|
93
|
+
const lane = laneOfAgent({ agent, session: clientCall.session, main, worktreeDirectory })
|
|
94
|
+
if (lane || !clientCall.subagent) return lane
|
|
95
|
+
const isLane = (name: string) => dependencies.isLaneName(main, name)
|
|
96
|
+
const coordinator = isCoordinatorAgent({ agent, session: clientCall.session, main, isLane })
|
|
97
|
+
return coordinator ? subagentLabel(clientCall.subagent) : null
|
|
98
|
+
}
|
|
99
|
+
|
|
78
100
|
/**
|
|
79
101
|
* The gate for a call that would open a permission dialog in a lane: it records the call as a request the watcher
|
|
80
102
|
* reports, waits for the user's answer to the coordinator's dialog, and allows or denies. The lane is the agent of the
|
|
81
|
-
* pane, never the directory of the call
|
|
82
|
-
* (the multiplexer or git cannot be asked), it
|
|
103
|
+
* pane, never the directory of the call; a subagent of the coordinator is held like one, under its own name. Outside a
|
|
104
|
+
* lane, for a question or a call too large to store, and on any failure (the multiplexer or git cannot be asked), it
|
|
105
|
+
* decides nothing, so the dialog stays.
|
|
83
106
|
*/
|
|
84
107
|
export const runGate = async (
|
|
85
108
|
clientCall: ClientCall,
|
|
@@ -92,7 +115,7 @@ export const runGate = async (
|
|
|
92
115
|
if (agent === null || agent.cwd === null) return null
|
|
93
116
|
const main = dependencies.mainCheckoutOf(agent.cwd)
|
|
94
117
|
const { worktreeDirectory, waitMinutes } = gateSettingsOf(dependencies.config(main))
|
|
95
|
-
const lane =
|
|
118
|
+
const lane = holderOf(clientCall, agent, main, worktreeDirectory, dependencies)
|
|
96
119
|
if (!lane) return null
|
|
97
120
|
const { cwd, tool, input } = clientCall
|
|
98
121
|
const request = openRequest(main, { lane, cwd, tool, input }, dependencies)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** The subagent a hook call comes from: Claude Code's `agent_id` and `agent_type`. */
|
|
2
|
+
export type SubagentCall = { id: string; type: string | null }
|
|
3
|
+
|
|
4
|
+
/** `agent_id` as Claude Code writes it into the subagent's log (`agent-<id>.jsonl`): lower-case hexadecimal. */
|
|
5
|
+
const AGENT_ID = /^[0-9a-f]{8,64}$/
|
|
6
|
+
const AGENT_TYPE = /^[a-z0-9][a-z0-9-]{0,31}$/
|
|
7
|
+
const ID_LENGTH = 8
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The name a subagent of the coordinator is held under, in the place of a lane's name: `subagent-<type>-<id start>`.
|
|
11
|
+
* The type is a plain word or `agent`, so nothing the subagent could choose freely reaches the coordinator's question;
|
|
12
|
+
* null for an id of another shape. The herd derives the same name from the subagent's log (contract test).
|
|
13
|
+
*/
|
|
14
|
+
export const subagentLabel = ({ id, type }: SubagentCall): string | null => {
|
|
15
|
+
if (!AGENT_ID.test(id)) return null
|
|
16
|
+
const word = type !== null && AGENT_TYPE.test(type) ? type : 'agent'
|
|
17
|
+
return `subagent-${word}-${id.slice(0, ID_LENGTH)}`
|
|
18
|
+
}
|
|
@@ -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: '
|
|
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
|
-
|
|
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
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
|
|
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
|
}
|