@pmelab/gtd 65.0.0 → 66.0.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 (63) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +3 -2
  3. package/claude/hooks/drive.ts +10 -1
  4. package/claude/hooks/git.ts +2 -2
  5. package/claude/hooks/pr.ts +80 -20
  6. package/claude/hooks/register.tsx +19 -4
  7. package/dist/chunks/{flows-DGs0W3HI.mjs → flows-CmQplvh1.mjs} +134 -40
  8. package/dist/chunks/{steering-DEh_MS40.mjs → steering-CMrePUiY.mjs} +115 -17
  9. package/dist/chunks/{testkit-CgrLmpdJ.mjs → testkit-BJhAAVGt.mjs} +179 -34
  10. package/dist/chunks/{wire-BNS8ciNu.mjs → wire-xTbzFc26.mjs} +7 -3
  11. package/dist/flows.mjs +1 -1
  12. package/dist/gtd.bundle.mjs +430 -100
  13. package/dist/gtd.core.mjs +147 -134
  14. package/dist/steering.mjs +1 -1
  15. package/dist/testkit/gherkin.mjs +1 -1
  16. package/dist/testkit.mjs +2 -2
  17. package/dist/web/main.css +1 -1
  18. package/dist/web/main.js +185 -18
  19. package/dist/wire.mjs +1 -1
  20. package/dist/workflow-kit.mjs +9 -3
  21. package/package.json +1 -1
  22. package/src/cli/Cli.ts +1 -1
  23. package/src/cli/Program.ts +18 -2
  24. package/src/edge/Edge.ts +93 -11
  25. package/src/flows/helpers.ts +36 -18
  26. package/src/flows/index.test.ts +3 -0
  27. package/src/flows/index.ts +5 -0
  28. package/src/flows/runtime.ts +123 -16
  29. package/src/install/Install.ts +9 -2
  30. package/src/platform/Git.test.ts +7 -11
  31. package/src/platform/NodeSync.ts +2 -0
  32. package/src/platform/SyncIo.ts +2 -0
  33. package/src/platform/Workspace.test.ts +37 -0
  34. package/src/platform/Workspace.ts +36 -0
  35. package/src/replay/Replay.ts +33 -1
  36. package/src/replay/Trailers.ts +3 -0
  37. package/src/replay/Tree.ts +5 -0
  38. package/src/steering/FORMATS.md +3 -1
  39. package/src/steering/SteeringFormat.ts +13 -0
  40. package/src/steering/fixtures/review/sectioned.md +1 -1
  41. package/src/steering/fixtures/review/sectioned.read.json +0 -1
  42. package/src/steering/index.ts +1 -0
  43. package/src/steering/review.test.ts +181 -5
  44. package/src/steering/review.ts +160 -18
  45. package/src/step/RepoSnapshot.ts +4 -0
  46. package/src/step/planStep.ts +6 -6
  47. package/src/twins/EmittedScriptRecognizer.ts +1 -1
  48. package/src/twins/InMemRepo.ts +24 -3
  49. package/src/twins/Layers.ts +1 -0
  50. package/src/twins/SyncIo.test.ts +19 -4
  51. package/src/twins/SyncIo.ts +13 -1
  52. package/src/wire/BeatDocument.test.ts +15 -0
  53. package/src/wire/BeatDocument.ts +8 -2
  54. package/src/wire/Demand.ts +4 -2
  55. package/src/wire/golden/1/beat-capture.json +1 -0
  56. package/src/wire/golden/1/beat-message.json +1 -0
  57. package/src/wire/golden/1/beat-prompt.json +1 -0
  58. package/src/wire/golden/1/beat-script.json +1 -0
  59. package/src/wire/golden/1/beat-stalled.json +1 -0
  60. package/src/workflow/Workflow.ts +2 -0
  61. package/src/workflow-kit/index.test.ts +1 -0
  62. package/src/workflow-kit/index.ts +1 -0
  63. package/src/workflow-kit/scenarios.ts +8 -0
package/dist/steering.mjs CHANGED
@@ -1 +1 @@
1
- export { g as ANSWER_FIX, _ as ANSWER_LEAD, v as ANSWER_WONT_FIX, O as FIX_BOX, E as FREE_TEXT_PLACEHOLDER, k as NOISE_HEADING, y as REVIEW_SECTIONS, h as applySteeringEdits, A as findingEntries, I as formatFinding, m as freeFormFormat, D as isAnswered, f as listFormat, r as openQuestions, j as openThreadFindings, c as openThreads, t as parseCodeThreads, M as parseFootnotes, N as parseThreads, P as parseThreadsWithFindings, b as reviewChunks, x as reviewDecisions, S as reviewNotes, C as reviewRisks, w as reviewSections, i as sectionBodies, p as sectionFormat, a as sections, l as steeringFormatFor, n as stripCodeThreads, o as threads, T as unansweredNotes } from "./chunks/steering-DEh_MS40.mjs";
1
+ export { g as ANSWER_FIX, _ as ANSWER_LEAD, v as ANSWER_WONT_FIX, O as FIX_BOX, E as FREE_TEXT_PLACEHOLDER, k as NOISE_HEADING, y as REVIEW_SECTIONS, h as applySteeringEdits, A as findingEntries, I as formatFinding, m as freeFormFormat, D as isAnswered, f as listFormat, r as openQuestions, j as openThreadFindings, c as openThreads, t as parseCodeThreads, M as parseFootnotes, N as parseThreads, P as parseThreadsWithFindings, b as reviewChunks, x as reviewDecisions, S as reviewNotes, C as reviewRisks, w as reviewSections, i as sectionBodies, p as sectionFormat, a as sections, l as steeringFormatFor, n as stripCodeThreads, o as threads, T as unansweredNotes } from "./chunks/steering-CMrePUiY.mjs";
@@ -1,4 +1,4 @@
1
- import { n as runsItsScript, r as testRepo } from "../chunks/testkit-CgrLmpdJ.mjs";
1
+ import { n as runsItsScript, r as testRepo } from "../chunks/testkit-BJhAAVGt.mjs";
2
2
  import { pathToFileURL } from "node:url";
3
3
  import { resolve } from "node:path";
4
4
  import { After, AfterAll, Given, QuickPickleWorld, Then, When, setWorldConstructor } from "quickpickle";
package/dist/testkit.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { H as installContext } from "./chunks/flows-DGs0W3HI.mjs";
2
- import { a as verdictWhen, i as verdict, n as runsItsScript, o as whenPrompted, r as testRepo, t as inTurn } from "./chunks/testkit-CgrLmpdJ.mjs";
1
+ import { G as installContext } from "./chunks/flows-CmQplvh1.mjs";
2
+ import { a as verdictWhen, i as verdict, n as runsItsScript, o as whenPrompted, r as testRepo, t as inTurn } from "./chunks/testkit-BJhAAVGt.mjs";
3
3
  export { inTurn, installContext, runsItsScript, testRepo, verdict, verdictWhen, whenPrompted };
package/dist/web/main.css CHANGED
@@ -1,2 +1,2 @@
1
1
  /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--radius-lg:.5rem;--radius-xl:.75rem;--ease-out:cubic-bezier(0, 0, .2, 1);--animate-spin:spin 1s linear infinite;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-page:#111;--color-surface:#222;--color-divider:#3a3a3a;--color-border:#6e6e6e;--color-text:#eee;--color-muted:#b4b4b4;--color-accent:#0090ff;--color-accent-pressed:#3b9eff;--color-link:#70b8ff;--color-disabled:#606060;--color-warning:#ffca16;--color-danger:#ff9592;--color-diff-add:#113b29;--color-diff-del:#500f1c;--color-heading-a:#baa7ff;--color-heading-b:#4ccce6;--color-heading-c:#1fd8a4;--color-code:#ffa057;--color-quote:#6e56cf;--color-syntax-kw:#baa7ff;--color-syntax-str:#ffca16;--color-syntax-num:#4ccce6;--color-syntax-typ:#1fd8a4;--color-syntax-com:#b4b4b4;--text-body:16px;--text-body--line-height:1.5;--text-large:20px;--text-large--line-height:1.3;--text-small:13px;--text-small--line-height:1.3}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-x-0{inset-inline:0}.top-0{top:0}.top-1{top:var(--spacing)}.right-1{right:var(--spacing)}.right-3{right:calc(var(--spacing) * 3)}.bottom-3{bottom:calc(var(--spacing) * 3)}.isolate{isolation:isolate}.z-10{z-index:10}.z-50{z-index:50}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-0{margin:0}.m-3{margin:calc(var(--spacing) * 3)}.-mx-3{margin-inline:calc(var(--spacing) * -3)}.mx-3{margin-inline:calc(var(--spacing) * 3)}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.list-item{display:list-item}.table{display:table}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.h-1{height:var(--spacing)}.h-\[600px\]{height:600px}.h-\[2000px\]{height:2000px}.h-\[3000px\]{height:3000px}.h-auto{height:auto}.h-dvh{height:100dvh}.h-full{height:100%}.max-h-full{max-height:100%}.min-h-0{min-height:0}.min-h-11{min-height:calc(var(--spacing) * 11)}.min-h-32{min-height:calc(var(--spacing) * 32)}.w-9{width:calc(var(--spacing) * 9)}.w-full{width:100%}.max-w-\[390px\]{max-width:390px}.max-w-\[430px\]{max-width:430px}.max-w-\[calc\(100\%-1\.5rem\)\]{max-width:calc(100% - 1.5rem)}.max-w-full{max-width:100%}.min-w-0{min-width:0}.min-w-11{min-width:calc(var(--spacing) * 11)}.min-w-max{min-width:max-content}.flex-1{flex:1}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-\[deck-item-in_140ms_ease-out\]{animation:.14s ease-out deck-item-in}.animate-\[scrim-in_160ms_ease-out\]{animation:.16s ease-out scrim-in}.animate-\[scrim-out_160ms_ease-out_forwards\]{animation:.16s ease-out forwards scrim-out}.animate-\[sheet-in_220ms_cubic-bezier\(0\.2\,0\,0\,1\)\]{animation:.22s cubic-bezier(.2,0,0,1) sheet-in}.animate-\[sheet-out_160ms_ease-out_forwards\]{animation:.16s ease-out forwards sheet-out}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.touch-manipulation{touch-action:manipulation}.resize-none{resize:none}.resize-y{resize:vertical}.list-outside{list-style-position:outside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.flex-col{flex-direction:column}.place-items-center{place-items:center}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.self-center{align-self:center}.self-end{align-self:flex-end}.self-start{align-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-t-xl{border-top-left-radius:var(--radius-xl);border-top-right-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-accent{border-color:var(--color-accent)}.border-border{border-color:var(--color-border)}.border-danger{border-color:var(--color-danger)}.border-divider{border-color:var(--color-divider)}.border-quote{border-color:var(--color-quote)}.border-transparent{border-color:#0000}.border-t-transparent{border-top-color:#0000}.border-l-accent{border-left-color:var(--color-accent)}.bg-accent{background-color:var(--color-accent)}.bg-danger{background-color:var(--color-danger)}.bg-diff-add{background-color:var(--color-diff-add)}.bg-diff-del{background-color:var(--color-diff-del)}.bg-divider{background-color:var(--color-divider)}.bg-muted{background-color:var(--color-muted)}.bg-page{background-color:var(--color-page)}.bg-page\/60{background-color:#1119}@supports (color:color-mix(in lab, red, red)){.bg-page\/60{background-color:color-mix(in oklab, var(--color-page) 60%, transparent)}}.bg-surface{background-color:var(--color-surface)}.bg-transparent{background-color:#0000}.bg-warning{background-color:var(--color-warning)}.p-0{padding:0}.p-1{padding:var(--spacing)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.px-0{padding-inline:0}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.pt-0\.5{padding-top:calc(var(--spacing) * .5)}.pt-1{padding-top:var(--spacing)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pb-0{padding-bottom:0}.pb-1{padding-bottom:var(--spacing)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pl-5{padding-left:calc(var(--spacing) * 5)}.pl-7{padding-left:calc(var(--spacing) * 7)}.pl-8{padding-left:calc(var(--spacing) * 8)}.pl-13{padding-left:calc(var(--spacing) * 13)}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-body{font-size:var(--text-body);line-height:var(--tw-leading,var(--text-body--line-height))}.text-large{font-size:var(--text-large);line-height:var(--tw-leading,var(--text-large--line-height))}.text-small{font-size:var(--text-small);line-height:var(--tw-leading,var(--text-small--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-6{--tw-leading:calc(var(--spacing) * 6);line-height:calc(var(--spacing) * 6)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-all{word-break:break-all}.whitespace-pre{white-space:pre}.text-black{color:var(--color-black)}.text-code{color:var(--color-code)}.text-danger{color:var(--color-danger)}.text-heading-a{color:var(--color-heading-a)}.text-heading-b{color:var(--color-heading-b)}.text-heading-c{color:var(--color-heading-c)}.text-inherit{color:inherit}.text-link{color:var(--color-link)}.text-muted{color:var(--color-muted)}.text-page{color:var(--color-page)}.text-syntax-com{color:var(--color-syntax-com)}.text-syntax-kw{color:var(--color-syntax-kw)}.text-syntax-num{color:var(--color-syntax-num)}.text-syntax-str{color:var(--color-syntax-str)}.text-syntax-typ{color:var(--color-syntax-typ)}.text-text{color:var(--color-text)}.text-warning{color:var(--color-warning)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[0_-8px_32px_rgba\(0\,0\,0\,0\.5\)\]{--tw-shadow:0 -8px 32px var(--tw-shadow-color,#00000080);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.outline-2{outline-style:var(--tw-outline-style);outline-width:2px}.outline-offset-2{outline-offset:2px}.outline-muted{outline-color:var(--color-muted)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[background-color\,border-color\]{transition-property:background-color,border-color;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[scale\]{transition-property:scale;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.marker\:text-link ::marker{color:var(--color-link)}.marker\:text-link::marker{color:var(--color-link)}.marker\:text-link ::-webkit-details-marker{color:var(--color-link)}.marker\:text-link::-webkit-details-marker{color:var(--color-link)}.focus-visible\:outline:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:-outline-offset-2:focus-visible{outline-offset:calc(2px * -1)}.focus-visible\:outline-accent:focus-visible{outline-color:var(--color-accent)}.active\:scale-96:active{--tw-scale-x:96%;--tw-scale-y:96%;--tw-scale-z:96%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:scale-\[0\.99\]:active{scale:.99}.active\:bg-accent-pressed:active{background-color:var(--color-accent-pressed)}.active\:bg-border:active{background-color:var(--color-border)}.active\:bg-surface:active{background-color:var(--color-surface)}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:bg-disabled:disabled{background-color:var(--color-disabled)}.disabled\:bg-surface:disabled{background-color:var(--color-surface)}.disabled\:text-disabled:disabled{color:var(--color-disabled)}.disabled\:text-muted:disabled{color:var(--color-muted)}.disabled\:opacity-60:disabled{opacity:.6}}.diff-dimmed{opacity:.85}body{background:var(--color-page);color:var(--color-text);overflow-wrap:anywhere;margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}input[type=checkbox],input[type=radio]{accent-color:var(--color-accent);flex-shrink:0;width:1.25rem;height:1.25rem;margin:0}textarea{border:1px solid var(--color-border);background:var(--color-surface);width:100%;color:var(--color-text);font:inherit;border-radius:.25rem;padding:.5rem;font-size:16px}textarea:focus-visible{outline:2px solid var(--color-accent);outline-offset:-1px}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important}}@keyframes scrim-in{0%{opacity:0}}@keyframes sheet-in{0%{transform:translateY(100%)}}@keyframes sheet-out{to{transform:translateY(100%)}}@keyframes scrim-out{to{opacity:0}}@keyframes deck-item-in{0%{opacity:.2}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@keyframes spin{to{transform:rotate(360deg)}}
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--radius-lg:.5rem;--radius-xl:.75rem;--ease-out:cubic-bezier(0, 0, .2, 1);--animate-spin:spin 1s linear infinite;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-page:#111;--color-surface:#222;--color-divider:#3a3a3a;--color-border:#6e6e6e;--color-text:#eee;--color-muted:#b4b4b4;--color-accent:#0090ff;--color-accent-pressed:#3b9eff;--color-link:#70b8ff;--color-disabled:#606060;--color-warning:#ffca16;--color-danger:#ff9592;--color-diff-add:#113b29;--color-diff-del:#500f1c;--color-heading-a:#baa7ff;--color-heading-b:#4ccce6;--color-heading-c:#1fd8a4;--color-code:#ffa057;--color-quote:#6e56cf;--color-syntax-kw:#baa7ff;--color-syntax-str:#ffca16;--color-syntax-num:#4ccce6;--color-syntax-typ:#1fd8a4;--color-syntax-com:#b4b4b4;--text-body:16px;--text-body--line-height:1.5;--text-large:20px;--text-large--line-height:1.3;--text-small:13px;--text-small--line-height:1.3}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-x-0{inset-inline:0}.top-0{top:0}.top-1{top:var(--spacing)}.right-1{right:var(--spacing)}.right-3{right:calc(var(--spacing) * 3)}.bottom-3{bottom:calc(var(--spacing) * 3)}.isolate{isolation:isolate}.z-10{z-index:10}.z-50{z-index:50}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-0{margin:0}.m-3{margin:calc(var(--spacing) * 3)}.-mx-3{margin-inline:calc(var(--spacing) * -3)}.mx-3{margin-inline:calc(var(--spacing) * 3)}.mx-auto{margin-inline:auto}.my-1{margin-block:var(--spacing)}.my-2{margin-block:calc(var(--spacing) * 2)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.list-item{display:list-item}.table{display:table}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.h-1{height:var(--spacing)}.h-\[600px\]{height:600px}.h-\[2000px\]{height:2000px}.h-\[3000px\]{height:3000px}.h-auto{height:auto}.h-dvh{height:100dvh}.h-full{height:100%}.max-h-full{max-height:100%}.min-h-0{min-height:0}.min-h-11{min-height:calc(var(--spacing) * 11)}.min-h-32{min-height:calc(var(--spacing) * 32)}.w-9{width:calc(var(--spacing) * 9)}.w-full{width:100%}.max-w-\[390px\]{max-width:390px}.max-w-\[430px\]{max-width:430px}.max-w-\[calc\(100\%-1\.5rem\)\]{max-width:calc(100% - 1.5rem)}.max-w-full{max-width:100%}.min-w-0{min-width:0}.min-w-11{min-width:calc(var(--spacing) * 11)}.min-w-max{min-width:max-content}.flex-1{flex:1}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-\[deck-item-in_140ms_ease-out\]{animation:.14s ease-out deck-item-in}.animate-\[scrim-in_160ms_ease-out\]{animation:.16s ease-out scrim-in}.animate-\[scrim-out_160ms_ease-out_forwards\]{animation:.16s ease-out forwards scrim-out}.animate-\[sheet-in_220ms_cubic-bezier\(0\.2\,0\,0\,1\)\]{animation:.22s cubic-bezier(.2,0,0,1) sheet-in}.animate-\[sheet-out_160ms_ease-out_forwards\]{animation:.16s ease-out forwards sheet-out}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.touch-manipulation{touch-action:manipulation}.resize-none{resize:none}.resize-y{resize:vertical}.list-outside{list-style-position:outside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.flex-col{flex-direction:column}.place-items-center{place-items:center}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.self-center{align-self:center}.self-end{align-self:flex-end}.self-start{align-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-t-xl{border-top-left-radius:var(--radius-xl);border-top-right-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-accent{border-color:var(--color-accent)}.border-border{border-color:var(--color-border)}.border-danger{border-color:var(--color-danger)}.border-divider{border-color:var(--color-divider)}.border-quote{border-color:var(--color-quote)}.border-transparent{border-color:#0000}.border-t-transparent{border-top-color:#0000}.border-l-accent{border-left-color:var(--color-accent)}.bg-accent{background-color:var(--color-accent)}.bg-danger{background-color:var(--color-danger)}.bg-diff-add{background-color:var(--color-diff-add)}.bg-diff-del{background-color:var(--color-diff-del)}.bg-divider{background-color:var(--color-divider)}.bg-muted{background-color:var(--color-muted)}.bg-page{background-color:var(--color-page)}.bg-page\/60{background-color:#1119}@supports (color:color-mix(in lab, red, red)){.bg-page\/60{background-color:color-mix(in oklab, var(--color-page) 60%, transparent)}}.bg-surface{background-color:var(--color-surface)}.bg-transparent{background-color:#0000}.bg-warning{background-color:var(--color-warning)}.p-0{padding:0}.p-1{padding:var(--spacing)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.px-0{padding-inline:0}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.pt-0\.5{padding-top:calc(var(--spacing) * .5)}.pt-1{padding-top:var(--spacing)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pb-0{padding-bottom:0}.pb-1{padding-bottom:var(--spacing)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pl-5{padding-left:calc(var(--spacing) * 5)}.pl-7{padding-left:calc(var(--spacing) * 7)}.pl-8{padding-left:calc(var(--spacing) * 8)}.pl-13{padding-left:calc(var(--spacing) * 13)}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-body{font-size:var(--text-body);line-height:var(--tw-leading,var(--text-body--line-height))}.text-large{font-size:var(--text-large);line-height:var(--tw-leading,var(--text-large--line-height))}.text-small{font-size:var(--text-small);line-height:var(--tw-leading,var(--text-small--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-6{--tw-leading:calc(var(--spacing) * 6);line-height:calc(var(--spacing) * 6)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.text-black{color:var(--color-black)}.text-code{color:var(--color-code)}.text-danger{color:var(--color-danger)}.text-heading-a{color:var(--color-heading-a)}.text-heading-b{color:var(--color-heading-b)}.text-heading-c{color:var(--color-heading-c)}.text-inherit{color:inherit}.text-link{color:var(--color-link)}.text-muted{color:var(--color-muted)}.text-page{color:var(--color-page)}.text-syntax-com{color:var(--color-syntax-com)}.text-syntax-kw{color:var(--color-syntax-kw)}.text-syntax-num{color:var(--color-syntax-num)}.text-syntax-str{color:var(--color-syntax-str)}.text-syntax-typ{color:var(--color-syntax-typ)}.text-text{color:var(--color-text)}.text-warning{color:var(--color-warning)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[0_-8px_32px_rgba\(0\,0\,0\,0\.5\)\]{--tw-shadow:0 -8px 32px var(--tw-shadow-color,#00000080);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.outline-2{outline-style:var(--tw-outline-style);outline-width:2px}.outline-offset-2{outline-offset:2px}.outline-muted{outline-color:var(--color-muted)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[background-color\,border-color\]{transition-property:background-color,border-color;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[scale\]{transition-property:scale;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.marker\:text-link ::marker{color:var(--color-link)}.marker\:text-link::marker{color:var(--color-link)}.marker\:text-link ::-webkit-details-marker{color:var(--color-link)}.marker\:text-link::-webkit-details-marker{color:var(--color-link)}.focus-visible\:outline:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:-outline-offset-2:focus-visible{outline-offset:calc(2px * -1)}.focus-visible\:outline-accent:focus-visible{outline-color:var(--color-accent)}.active\:scale-96:active{--tw-scale-x:96%;--tw-scale-y:96%;--tw-scale-z:96%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:scale-\[0\.99\]:active{scale:.99}.active\:bg-accent-pressed:active{background-color:var(--color-accent-pressed)}.active\:bg-border:active{background-color:var(--color-border)}.active\:bg-surface:active{background-color:var(--color-surface)}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:bg-disabled:disabled{background-color:var(--color-disabled)}.disabled\:bg-surface:disabled{background-color:var(--color-surface)}.disabled\:text-disabled:disabled{color:var(--color-disabled)}.disabled\:text-muted:disabled{color:var(--color-muted)}.disabled\:opacity-60:disabled{opacity:.6}}.diff-dimmed{opacity:.85}body{background:var(--color-page);color:var(--color-text);overflow-wrap:anywhere;margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}input[type=checkbox],input[type=radio]{accent-color:var(--color-accent);flex-shrink:0;width:1.25rem;height:1.25rem;margin:0}textarea{border:1px solid var(--color-border);background:var(--color-surface);width:100%;color:var(--color-text);font:inherit;border-radius:.25rem;padding:.5rem;font-size:16px}textarea:focus-visible{outline:2px solid var(--color-accent);outline-offset:-1px}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important}}@keyframes scrim-in{0%{opacity:0}}@keyframes sheet-in{0%{transform:translateY(100%)}}@keyframes sheet-out{to{transform:translateY(100%)}}@keyframes scrim-out{to{opacity:0}}@keyframes deck-item-in{0%{opacity:.2}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@keyframes spin{to{transform:rotate(360deg)}}
package/dist/web/main.js CHANGED
@@ -31877,6 +31877,7 @@ const buildHunkFile = (content, item, paragraph, token, sourceLine) => {
31877
31877
  ...parsed.line !== void 0 ? { line: parsed.line } : {},
31878
31878
  ...parsed.rangeEnd !== void 0 ? { rangeEnd: parsed.rangeEnd } : {},
31879
31879
  checked: item.checked === true,
31880
+ ...item.checked === null ? { plain: true } : {},
31880
31881
  sourceLine,
31881
31882
  endLine: hunkOwnEndLine(item, sourceLine),
31882
31883
  ...note.length > 0 ? {
@@ -31983,6 +31984,35 @@ const answerListOf = (content, body) => {
31983
31984
  })
31984
31985
  };
31985
31986
  };
31987
+ const COMMENT_LABEL_RE = /^(Impact|Fix):[ \t]*/;
31988
+ /** Splits a drafted comment at its `Impact:` / `Fix:` lines (outside fences) into labelled block sections; no labels is one What. */
31989
+ const commentSections = (comment) => {
31990
+ const parts = [{
31991
+ label: "What",
31992
+ lines: []
31993
+ }];
31994
+ let fence;
31995
+ for (const line of comment.split("\n")) {
31996
+ const marker = /^\s*(`{3,}|~{3,})/.exec(line)?.[1];
31997
+ if (marker !== void 0) {
31998
+ if (fence === void 0) fence = marker;
31999
+ else if (marker.startsWith(fence)) fence = void 0;
32000
+ }
32001
+ const label = fence === void 0 && marker === void 0 ? COMMENT_LABEL_RE.exec(line) : null;
32002
+ if (label) parts.push({
32003
+ label: label[1],
32004
+ lines: [line.slice(label[0].length)]
32005
+ });
32006
+ else parts.at(-1).lines.push(line);
32007
+ }
32008
+ return parts.map(({ label, lines }) => ({
32009
+ label,
32010
+ text: lines.join("\n").trim()
32011
+ })).filter(({ text }) => text.length > 0).map(({ label, text }) => ({
32012
+ label,
32013
+ body: blockNodesOfRun(text, parseMarkdown(text).children)
32014
+ }));
32015
+ };
31986
32016
  /** A leading severity line is the chunk's tier, never its prose. */
31987
32017
  const splitSeverity = (content, run) => {
31988
32018
  const first = run[0];
@@ -31996,28 +32026,52 @@ const splitSeverity = (content, run) => {
31996
32026
  leadingRun: run.slice(1)
31997
32027
  };
31998
32028
  };
32029
+ /** Every `listItem` under `node`, task or plain, at any depth. */
32030
+ const allListItems = (node) => {
32031
+ const items = [];
32032
+ const walk = (n) => {
32033
+ if (n.type === "listItem") items.push(n);
32034
+ n.children?.forEach((c) => walk(c));
32035
+ };
32036
+ walk(node);
32037
+ return items;
32038
+ };
31999
32039
  /** Splits one chunk's body nodes into its file pointers (hunk pointers are task items, collected recursively at ANY nesting depth via `taskItems` — a nested hunk is the same kind of hunk as a top-level one) and description prose. The description is the chunk's own leading run and never a node that contains a hunk pointer: it stops at the first node whose `taskItems` include a real pointer (at any depth — a blockquote wrapping a pointer list stops it just as a top-level list would). Every node kind in what's left before that point contributes — a `footnoteDefinition` is the one exception, excluded by `blockNodesOfRun` itself (it's a note, never document content). */
32000
- const parseChunkBody = (content, lines, title, body, answerable) => {
32040
+ const parseChunkBody = (content, lines, title, body, answerable, finding) => {
32001
32041
  const answer = answerable ? answerListOf(content, body) : void 0;
32002
32042
  const isAnswer = (n) => answer !== void 0 && (n === answer.list || n === body[answer.lead]);
32003
- const firstPointerIndex = body.findIndex((n) => !isAnswer(n) && taskItems(n).some((item) => hasPointerToken(content, item)));
32043
+ const pointerItems = (n) => finding ? allListItems(n) : taskItems(n);
32044
+ const firstPointerIndex = body.findIndex((n) => !isAnswer(n) && pointerItems(n).some((item) => hasPointerToken(content, item)));
32004
32045
  const run = (firstPointerIndex === -1 ? body : body.slice(0, firstPointerIndex)).filter((n) => !isAnswer(n));
32005
- const { severity, leadingRun } = splitSeverity(content, run);
32046
+ const draftIndex = answer !== void 0 ? body.findIndex((n, i) => !isAnswer(n) && n.type === "blockquote" && body[i - 1]?.type === "paragraph" && /^draft pr comment:?$/i.test(sourceText(content, body[i - 1]).trim())) : -1;
32047
+ const comment = draftIndex === -1 ? void 0 : content.slice(body[draftIndex].position.start.offset, body[draftIndex].position.end.offset).split("\n").map((l) => l.replace(/^\s*> ?/, "")).join("\n").trim();
32048
+ const draftNodes = new Set(draftIndex === -1 ? [] : [body[draftIndex], body[draftIndex - 1]]);
32049
+ const { severity, leadingRun: fullRun } = splitSeverity(content, run);
32050
+ const leadingRun = fullRun.filter((n) => !draftNodes.has(n));
32006
32051
  const descriptionNodes = blockNodesOfRun(content, leadingRun);
32007
32052
  const descriptionInline = collapseImages(blockRunInline(content, leadingRun));
32008
- const description = leadingRun.filter((n) => n.type !== "footnoteDefinition").map((n) => sourceText(content, n)).join(" ").trim();
32053
+ const textOf = (nodes) => nodes.filter((n) => n.type !== "footnoteDefinition").map((n) => sourceText(content, n)).join(" ").trim();
32054
+ const description = textOf(fullRun);
32055
+ const detail = textOf(leadingRun);
32009
32056
  const files = [];
32010
32057
  const errors = [];
32011
- for (const item of body.filter((n) => !isAnswer(n)).flatMap((n) => taskItems(n))) {
32058
+ for (const item of body.filter((n) => !isAnswer(n)).flatMap((n) => pointerItems(n))) {
32012
32059
  const parsed = parseHunk(content, lines, title, item);
32013
32060
  if (!parsed) continue;
32061
+ if (finding && item.checked !== null) errors.push({
32062
+ message: `Chunk "${title}" finding location ${pointerDisplay(parsed.file)} must be a plain list item — drop the checkbox`,
32063
+ line: parsed.file.sourceLine,
32064
+ range: lineRange(lines, parsed.file.sourceLine)
32065
+ });
32014
32066
  files.push(parsed.file);
32015
32067
  errors.push(...parsed.errors);
32016
32068
  }
32017
32069
  return {
32018
32070
  description,
32071
+ detail,
32019
32072
  descriptionNodes,
32020
32073
  descriptionInline,
32074
+ ...comment === void 0 ? {} : { comment },
32021
32075
  files,
32022
32076
  errors,
32023
32077
  ...answer === void 0 ? {} : {
@@ -32091,7 +32145,7 @@ const parseChangesets = (tree, content, lines) => {
32091
32145
  const errors = [];
32092
32146
  for (const { heading, headingLine, body, section } of splitChunks(tree, content).chunks) {
32093
32147
  const title = headingText$1(content, heading);
32094
- const { description, descriptionNodes, descriptionInline, files, answers, answerLine, severity, boxed, errors: bodyErrors } = parseChunkBody(content, lines, title, body, section !== "walkthrough");
32148
+ const { description, detail, descriptionNodes, descriptionInline, comment, files, answers, answerLine, severity, boxed, errors: bodyErrors } = parseChunkBody(content, lines, title, body, section !== "walkthrough", section === "findings");
32095
32149
  errors.push(...bodyErrors);
32096
32150
  if (files.length === 0 && boxed) errors.push({
32097
32151
  message: `Chunk "${title}" has no file pointers`,
@@ -32101,8 +32155,10 @@ const parseChangesets = (tree, content, lines) => {
32101
32155
  changesets.push({
32102
32156
  title,
32103
32157
  description,
32158
+ detail,
32104
32159
  descriptionNodes,
32105
32160
  descriptionInline,
32161
+ ...comment === void 0 ? {} : { comment },
32106
32162
  files,
32107
32163
  headingLine,
32108
32164
  ...answers === void 0 ? {} : {
@@ -32248,7 +32304,7 @@ const decisionNodes = (lines, chunk, end) => {
32248
32304
  selectionRange: lineRange(lines, chunk.answerLine)
32249
32305
  }];
32250
32306
  };
32251
- const isOpenChunk = (chunk, markers) => chunk.files.some((file) => !file.checked) || markers > 0 || chunk.answers !== void 0 && decisionOf$1(chunk) === void 0;
32307
+ const isOpenChunk = (chunk, markers) => chunk.files.some((file) => !file.plain && !file.checked) || markers > 0 || chunk.answers !== void 0 && decisionOf$1(chunk) === void 0;
32252
32308
  /**
32253
32309
  * Document outline for `.gtd/REVIEW.md`: the chunks still left to review — an
32254
32310
  * unchecked hunk, a footnote (so a human's comment never vanishes with the
@@ -32279,7 +32335,7 @@ const reviewOutline = (content) => {
32279
32335
  ];
32280
32336
  if (!(sections.length > 0 && chunk.section === void 0) && !isOpenChunk(chunk, chunkMarkers.length)) return [];
32281
32337
  const node = {
32282
- name: chunk.files.length === 0 ? chunk.title : `${chunk.title} (${checkedCount}/${chunk.files.length})`,
32338
+ name: chunk.files.length === 0 || chunk.section === "findings" ? chunk.title : `${chunk.title} (${checkedCount}/${chunk.files.length})`,
32283
32339
  range: spanRange(lines, start, end),
32284
32340
  selectionRange: lineRange(lines, start),
32285
32341
  ...children.length > 0 ? { children } : {}
@@ -32342,6 +32398,43 @@ const footnoteBlockEnd = (content, tree, cursorLine) => {
32342
32398
  const block = blockNodeAt(tree, cursorLine);
32343
32399
  return block?.position ? toLspPosition(block.position.end).line : cursorLine;
32344
32400
  };
32401
+ const FINDING_ACTIONS = [
32402
+ {
32403
+ title: "gtd: accept finding",
32404
+ index: 0
32405
+ },
32406
+ {
32407
+ title: "gtd: reject finding",
32408
+ index: 1
32409
+ },
32410
+ {
32411
+ title: "gtd: answer finding",
32412
+ index: 2
32413
+ }
32414
+ ];
32415
+ /** Accept / reject / answer for one finding chunk; the one matching its single current tick is left out. */
32416
+ const findingActions = (content, chunk, chunkIndex) => {
32417
+ if (chunk.answers?.length !== 3) return [];
32418
+ const ticked = chunk.answers.flatMap((o, i) => o.checked ? [i] : []);
32419
+ const current = ticked.length === 1 ? ticked[0] : void 0;
32420
+ const items = answerItemsOf(content, chunkIndex);
32421
+ return FINDING_ACTIONS.filter((a) => a.index !== current).flatMap((a) => {
32422
+ const result = decisionEdits(content, chunkIndex, a.index, {});
32423
+ if (!result.ok) return [];
32424
+ const slot = items[2];
32425
+ const from = slot ? firstParagraphContentOffset(slot) : void 0;
32426
+ const to = slot?.children.find((c) => c.type === "paragraph")?.position?.end.offset;
32427
+ const select = a.index === 2 && from !== void 0 && to !== void 0 ? {
32428
+ start: toLspPositionFromOffset(content, from),
32429
+ end: toLspPositionFromOffset(content, to)
32430
+ } : void 0;
32431
+ return [{
32432
+ title: a.title,
32433
+ edits: result.edits,
32434
+ ...select ? { select } : {}
32435
+ }];
32436
+ });
32437
+ };
32345
32438
  /**
32346
32439
  * Actions for `.gtd/REVIEW.md`: "check/uncheck this hunk" when `range` sits
32347
32440
  * on a hunk line, "check/uncheck all hunks" when `range` sits anywhere in a
@@ -32359,7 +32452,7 @@ const reviewActions = (content, range) => {
32359
32452
  const actions = [...threadReplyActions(content, range.start)];
32360
32453
  changesets.forEach((chunk, i) => {
32361
32454
  const hunk = chunk.files.find((file) => cursorLine >= file.sourceLine && cursorLine <= file.endLine);
32362
- if (hunk) {
32455
+ if (hunk && !hunk.plain) {
32363
32456
  const edit = toggleFilePointer(content, hunk.sourceLine);
32364
32457
  if (edit) actions.push({
32365
32458
  title: hunk.checked ? "gtd: uncheck this hunk" : "gtd: check this hunk",
@@ -32367,7 +32460,8 @@ const reviewActions = (content, range) => {
32367
32460
  });
32368
32461
  }
32369
32462
  const chunkEnd = chunk.section === void 0 ? Math.max(chunk.headingLine, (changesets[i + 1]?.headingLine ?? lines.length) - 1) : endLines.get(chunk.headingLine) ?? chunk.headingLine;
32370
- if (chunk.files.length > 0 && cursorLine >= chunk.headingLine && cursorLine <= chunkEnd) {
32463
+ if (chunk.section === "findings" && cursorLine >= chunk.headingLine && cursorLine <= chunkEnd) actions.push(...findingActions(content, chunk, i));
32464
+ if (chunk.section !== "findings" && chunk.files.length > 0 && cursorLine >= chunk.headingLine && cursorLine <= chunkEnd) {
32371
32465
  const edits = toggleChunkEdits(content, chunk.headingLine);
32372
32466
  if (edits.length > 0) {
32373
32467
  const checkedCount = chunk.files.filter((file) => file.checked).length;
@@ -32434,9 +32528,12 @@ const hunkLinkFor = (content, item) => {
32434
32528
  const reviewDocumentLinks = (content) => {
32435
32529
  const tree = parseMarkdown(content);
32436
32530
  const links = [];
32437
- for (const block of splitChunks(tree, content).chunks) for (const item of block.body.flatMap((n) => taskItems(n))) {
32438
- const link = hunkLinkFor(content, item);
32439
- if (link) links.push(link);
32531
+ for (const block of splitChunks(tree, content).chunks) {
32532
+ const items = block.body.flatMap((n) => block.section === "findings" ? allListItems(n) : taskItems(n));
32533
+ for (const item of items) {
32534
+ const link = hunkLinkFor(content, item);
32535
+ if (link) links.push(link);
32536
+ }
32440
32537
  }
32441
32538
  return links;
32442
32539
  };
@@ -32448,9 +32545,10 @@ const reviewView = (content) => {
32448
32545
  ...shortHash ? { header: shortHash } : {},
32449
32546
  nodes: changesets.map((chunk, chunkIndex) => ({
32450
32547
  title: chunk.title,
32451
- detail: chunk.description,
32548
+ detail: chunk.detail,
32452
32549
  detailInline: chunk.descriptionInline,
32453
32550
  body: chunk.descriptionNodes,
32551
+ ...chunk.comment === void 0 ? {} : { comment: commentSections(chunk.comment) },
32454
32552
  ...chunk.severity === void 0 ? {} : { status: chunk.severity.label },
32455
32553
  anchor: {
32456
32554
  kind: "chunk",
@@ -32475,7 +32573,7 @@ const reviewView = (content) => {
32475
32573
  path: file.path,
32476
32574
  ...file.line !== void 0 ? { line: file.line } : {},
32477
32575
  ...file.rangeEnd !== void 0 ? { endLine: file.rangeEnd } : {},
32478
- checked: file.checked,
32576
+ ...file.plain ? {} : { checked: file.checked },
32479
32577
  ...file.note !== void 0 ? {
32480
32578
  detail: file.note,
32481
32579
  detailInline: file.noteInline ?? []
@@ -32630,7 +32728,7 @@ const reviewApply = (content, anchor, opts) => {
32630
32728
  const checked = opts.checked ?? true;
32631
32729
  if (anchor.kind === "hunk") {
32632
32730
  const file = changesets[anchor.chunkIndex]?.files[anchor.index];
32633
- if (!file) return {
32731
+ if (!file || file.plain) return {
32634
32732
  ok: false,
32635
32733
  reason: "anchor-not-found"
32636
32734
  };
@@ -32641,7 +32739,7 @@ const reviewApply = (content, anchor, opts) => {
32641
32739
  }
32642
32740
  if (anchor.kind === "chunk") {
32643
32741
  const chunk = changesets[anchor.index];
32644
- if (!chunk) return {
32742
+ if (!chunk || chunk.section === "findings") return {
32645
32743
  ok: false,
32646
32744
  reason: "anchor-not-found"
32647
32745
  };
@@ -35192,7 +35290,7 @@ const Hunk = ({ node, diff, index, total, checked, hasNote, note, thread, onTogg
35192
35290
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DiffBody, { diff }),
35193
35291
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
35194
35292
  className: "flex flex-col gap-2 p-3",
35195
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApproveRow, {
35293
+ children: [node.checked !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApproveRow, {
35196
35294
  checked,
35197
35295
  onToggle,
35198
35296
  onApprove
@@ -35592,9 +35690,58 @@ const ChunkMark = ({ chunk, chunkIndex, state }) => {
35592
35690
  })
35593
35691
  });
35594
35692
  };
35693
+ const locationOf = (hunk) => `${hunk.path ?? hunk.title}${hunk.line === void 0 ? "" : `#${hunk.line}${hunk.endLine === void 0 ? "" : `-${hunk.endLine}`}`}`;
35694
+ const CommentSections = ({ chunk, chunkIndex }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
35695
+ "data-testid": `chunk-comment-${chunkIndex}`,
35696
+ className: "mt-2 min-w-0 break-words",
35697
+ children: (chunk.comment ?? []).map((section, si) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
35698
+ "data-testid": `chunk-comment-${chunkIndex}-${section.label.toLowerCase()}`,
35699
+ className: "mb-2 min-w-0",
35700
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", {
35701
+ className: "m-0 text-small font-semibold text-muted",
35702
+ children: section.label
35703
+ }), section.body.map((node, i) => node.block?.kind === "code" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
35704
+ className: "my-1 whitespace-pre-wrap break-words rounded bg-surface p-3 text-small text-code",
35705
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: node.block.text ?? "" })
35706
+ }, i) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
35707
+ className: "-mx-3",
35708
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProseBlocks, {
35709
+ nodes: [node],
35710
+ noteOverrides: {},
35711
+ onOpenNote: () => {},
35712
+ readOnly: true
35713
+ })
35714
+ }, i))]
35715
+ }, si))
35716
+ });
35717
+ const FindingHeader = ({ chunk, chunkIndex, hunks, expanded, onToggle, footnote }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
35718
+ variant: "ghost",
35719
+ "data-testid": `chunk-expand-${chunkIndex}`,
35720
+ "aria-expanded": expanded,
35721
+ onClick: onToggle,
35722
+ className: "min-h-11 min-w-0 flex-1 text-left",
35723
+ children: [
35724
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChunkSummary, {
35725
+ chunk,
35726
+ chunkIndex
35727
+ }),
35728
+ hunks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
35729
+ "data-testid": `chunk-location-${chunkIndex}`,
35730
+ className: "mt-0.5 break-all text-small text-muted",
35731
+ children: hunks.map(locationOf).join(", ")
35732
+ }),
35733
+ footnote && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
35734
+ "data-testid": `chunk-footnote-badge-${chunkIndex}`,
35735
+ className: "mt-1 text-small text-warning",
35736
+ children: "Note keeps this round open"
35737
+ })
35738
+ ]
35739
+ });
35595
35740
  const ChunkRow = ({ chunk, chunkIndex, state }) => {
35596
35741
  const hunks = hunksOf(chunk);
35597
35742
  const footnoteKeepsRoundOpen = state.hasNoteText(chunk);
35743
+ const [expanded, setExpanded] = (0, import_react.useState)(false);
35744
+ const expandable = chunk.decision !== void 0 && (chunk.comment ?? []).length > 0;
35598
35745
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
35599
35746
  "data-testid": `chunk-card-${chunkIndex}`,
35600
35747
  className: "border-b border-divider px-3 py-2.5",
@@ -35619,6 +35766,13 @@ const ChunkRow = ({ chunk, chunkIndex, state }) => {
35619
35766
  chunkIndex,
35620
35767
  state
35621
35768
  })]
35769
+ }) : expandable ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FindingHeader, {
35770
+ chunk,
35771
+ chunkIndex,
35772
+ hunks,
35773
+ expanded,
35774
+ onToggle: () => setExpanded(!expanded),
35775
+ footnote: footnoteKeepsRoundOpen
35622
35776
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
35623
35777
  variant: "ghost",
35624
35778
  "data-testid": `chunk-open-${chunkIndex}`,
@@ -35649,6 +35803,19 @@ const ChunkRow = ({ chunk, chunkIndex, state }) => {
35649
35803
  })
35650
35804
  ]
35651
35805
  }),
35806
+ expandable && expanded && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
35807
+ className: "min-w-0 pl-13",
35808
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommentSections, {
35809
+ chunk,
35810
+ chunkIndex
35811
+ }), hunks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
35812
+ variant: "secondary",
35813
+ "data-testid": `chunk-open-${chunkIndex}`,
35814
+ onClick: () => state.openChunk(chunkIndex),
35815
+ className: "min-h-11",
35816
+ children: "Open code"
35817
+ })]
35818
+ }),
35652
35819
  chunk.decision !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DecisionControl, {
35653
35820
  chunk,
35654
35821
  chunkIndex,
package/dist/wire.mjs CHANGED
@@ -1 +1 @@
1
- export { O as ACCESS_REFUSAL, _ as BEAT_KINDS, f as DEMAND_BRIEFING, x as EXIT_CODES, S as EXIT_OK, C as EXIT_RUNTIME_ERROR, w as EXIT_SIGINT, T as EXIT_SIGTERM, E as EXIT_STRAYS_FOUND, D as EXIT_USAGE_ERROR, k as PIN_PATH, A as RUN_FAILED_PATH, j as UNATTRIBUTED_MODEL, M as WIRE_SCHEMA, m as beatDocument, v as beatKindOf, y as demandOf, t as globMatches, i as judgeJson, s as landFields, c as landProseText, l as noopText, h as renderBeatJson, p as renderBeatPlain, N as renderFormat, u as renderLandJson, d as renderLandPlain, a as renderScriptJson, n as resolveSession, o as scriptFields, r as selectPath, b as stallDiagnosis, g as statusOf } from "./chunks/wire-BNS8ciNu.mjs";
1
+ export { O as ACCESS_REFUSAL, _ as BEAT_KINDS, f as DEMAND_BRIEFING, x as EXIT_CODES, S as EXIT_OK, C as EXIT_RUNTIME_ERROR, w as EXIT_SIGINT, T as EXIT_SIGTERM, E as EXIT_STRAYS_FOUND, D as EXIT_USAGE_ERROR, k as PIN_PATH, A as RUN_FAILED_PATH, j as UNATTRIBUTED_MODEL, M as WIRE_SCHEMA, m as beatDocument, v as beatKindOf, y as demandOf, t as globMatches, i as judgeJson, s as landFields, c as landProseText, l as noopText, h as renderBeatJson, p as renderBeatPlain, N as renderFormat, u as renderLandJson, d as renderLandPlain, a as renderScriptJson, n as resolveSession, o as scriptFields, r as selectPath, b as stallDiagnosis, g as statusOf } from "./chunks/wire-xTbzFc26.mjs";
@@ -1,5 +1,5 @@
1
- import { B as head, F as changesSince, G as mark, P as changes, X as read, Z as refuse, d as hasThreadFor, et as scope, g as requireReplies, v as requireThreadsClosed } from "./chunks/flows-DGs0W3HI.mjs";
2
- import { C as reviewRisks, N as parseThreads, S as reviewNotes, T as unansweredNotes, n as stripCodeThreads, x as reviewDecisions } from "./chunks/steering-DEh_MS40.mjs";
1
+ import { $ as read, J as mark, L as changes, R as changesSince, U as head, b as requireThreadsClosed, d as hasThreadFor, et as refuse, rt as scope, v as requireReplies } from "./chunks/flows-CmQplvh1.mjs";
2
+ import { C as reviewRisks, N as parseThreads, S as reviewNotes, T as unansweredNotes, n as stripCodeThreads, x as reviewDecisions } from "./chunks/steering-CMrePUiY.mjs";
3
3
  // src/workflow-kit/health.ts
4
4
  /** Hand a red suite that repeated attempts could not fix to a person, until `limit` rounds are spent. */
5
5
  const escalation = async (count, steps) => {
@@ -159,6 +159,12 @@ const removedAssertions = (changes) => changes.filter((c) => c.path.endsWith(".f
159
159
  lines
160
160
  };
161
161
  }).filter((r) => r.lines.length > 0);
162
+ /** Validator form of `removedAssertions`: lists each lost line by path and scenario, or `undefined` when none. */
163
+ const assertionErrors = (changes) => {
164
+ const lost = removedAssertions(changes);
165
+ if (lost.length === 0) return void 0;
166
+ return `Restore the assertions these .feature files lost:\n${lost.flatMap((r) => r.lines.map((a) => ` - ${r.path}: ${a.scenario}: ${a.line}`)).join("\n")}`;
167
+ };
162
168
  // src/workflow-kit/review.ts
163
169
  const collect = async (p, capture, round, requested) => {
164
170
  await p.collecting(capture, round);
@@ -423,4 +429,4 @@ const reviewToHandOff = async (base, p) => {
423
429
  return;
424
430
  }
425
431
  };
426
- export { assertionLines, escalation, fixFindings, gate, healthy, lensEntriesOf, lensOf, lensScopes, lensesOf, qualityLap, removedAssertions, review, reviewToHandOff, untilSettled };
432
+ export { assertionErrors, assertionLines, escalation, fixFindings, gate, healthy, lensEntriesOf, lensOf, lensScopes, lensesOf, qualityLap, removedAssertions, review, reviewToHandOff, untilSettled };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmelab/gtd",
3
- "version": "65.0.0",
3
+ "version": "66.0.0",
4
4
  "private": false,
5
5
  "description": "Git-aware CLI that emits the next prompt for an autonomous coding agent based on the current repository state",
6
6
  "bin": {
package/src/cli/Cli.ts CHANGED
@@ -615,7 +615,7 @@ The fixed answerer reads --answers and leaves out the questions it does not cove
615
615
  arity: { name: "file" },
616
616
  group: "Driver plumbing",
617
617
  summary: "Untick a review file's boxes",
618
- details: `Reset every ticked review box (- [x]) in <file> to - [ ], writing the file only when it changes; a missing file writes nothing. Reads no workflow and runs from any directory — the landing script at a review gate calls it, so no tick is ever committed.`,
618
+ details: `Reset every ticked review box (- [x]) in <file> to - [ ], writing the file only when it changes; a missing file writes nothing. Reads no workflow and runs from any directory — the landing script at a review gate calls it unless the next step is an agent turn, which keeps the ticks.`,
619
619
  },
620
620
  {
621
621
  token: "lsp",
@@ -911,7 +911,10 @@ const runRestoreCommand = (
911
911
  const tip = retained.value
912
912
 
913
913
  const headHash = yield* git.resolveRef("HEAD")
914
- const check = yield* restorability(git, headHash, tip)
914
+ // Abandon after a ship leaves HEAD on `<b>` at the bare ship commit: its `Gtd-Tip` is on the retained tip.
915
+ const shippedTip = parseCommitMessage(yield* git.lastCommitMessage(headHash)).tip
916
+ const atShip = shippedTip !== undefined && (yield* git.isAncestor(shippedTip, tip))
917
+ const check = atShip ? { ok: true as const } : yield* restorability(git, headHash, tip)
915
918
  if (!check.ok) {
916
919
  return yield* Effect.fail(
917
920
  new Error(
@@ -1286,6 +1289,17 @@ const computeNextMatch = (rest: Rest): Effect.Effect<NextMatch | null> =>
1286
1289
  (target) => (target === undefined ? null : { target }),
1287
1290
  )
1288
1291
 
1292
+ /** A validator retry's content is the correction; a session that cannot resume needs the original prompt too, and one that can hands a driver that loses it `fresh`. */
1293
+ const retryContent = (
1294
+ kind: BeatKind,
1295
+ rendered: RenderedRest,
1296
+ resumes: boolean,
1297
+ ): { readonly rendered: RenderedRest; readonly fresh?: string } => {
1298
+ if (kind !== "prompt" || rendered.original === undefined) return { rendered }
1299
+ const joined = `${rendered.original}\n\n${rendered.content}`
1300
+ return resumes ? { rendered, fresh: joined } : { rendered: { ...rendered, content: joined } }
1301
+ }
1302
+
1289
1303
  /** Everything one beat needs beyond the resolved rest itself, gathered once so plain/`--json` can never describe different rests for the same beat — built as `wire`'s `Demand` (what to do) and `BeatStatus` (what no driver branches on), then flattened by `beatDocument` into the one wire-shaped object plain/`--json` both render from. */
1290
1304
  const gatherBeatDocument = (
1291
1305
  rest: Rest,
@@ -1304,9 +1318,11 @@ const gatherBeatDocument = (
1304
1318
  ? resolvedValidate.script
1305
1319
  : undefined
1306
1320
  const log = yield* loopLogPath
1321
+ const retry = retryContent(kind, rendered, session?.resume === true)
1307
1322
  const demand = demandOf({
1308
- rendered,
1323
+ rendered: retry.rendered,
1309
1324
  kind,
1325
+ ...(retry.fresh !== undefined ? { fresh: retry.fresh } : {}),
1310
1326
  ...(session !== undefined
1311
1327
  ? { session: { id: session.sessionId, resume: session.resume } }
1312
1328
  : {}),