github-issue-tower-defence-management 1.185.1 → 1.186.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 (135) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +2 -0
  3. package/bin/adapter/entry-points/cli/index.js +2 -1
  4. package/bin/adapter/entry-points/cli/index.js.map +1 -1
  5. package/bin/adapter/entry-points/console/consoleOperationApi.js +43 -1
  6. package/bin/adapter/entry-points/console/consoleOperationApi.js.map +1 -1
  7. package/bin/adapter/entry-points/console/consoleProjectResolver.js +7 -1
  8. package/bin/adapter/entry-points/console/consoleProjectResolver.js.map +1 -1
  9. package/bin/adapter/entry-points/console/ui-dist/assets/index-wj6o8qvo.js +86 -0
  10. package/bin/adapter/entry-points/console/ui-dist/assets/index-yZy1TUOo.css +2 -0
  11. package/bin/adapter/entry-points/console/ui-dist/index.html +2 -2
  12. package/bin/adapter/entry-points/console/webServer.js +3 -0
  13. package/bin/adapter/entry-points/console/webServer.js.map +1 -1
  14. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +5 -1
  15. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  16. package/bin/adapter/entry-points/handlers/tokenStatusWriter.js +1 -0
  17. package/bin/adapter/entry-points/handlers/tokenStatusWriter.js.map +1 -1
  18. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +36 -0
  19. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
  20. package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js +12 -0
  21. package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js.map +1 -1
  22. package/bin/domain/entities/Project.js +11 -1
  23. package/bin/domain/entities/Project.js.map +1 -1
  24. package/bin/domain/usecases/HandleScheduledEventUseCase.js +12 -1
  25. package/bin/domain/usecases/HandleScheduledEventUseCase.js.map +1 -1
  26. package/bin/domain/usecases/IssueNoStatusUpdateUseCase.js +36 -0
  27. package/bin/domain/usecases/IssueNoStatusUpdateUseCase.js.map +1 -0
  28. package/bin/domain/usecases/IssueRejectionEvaluator.js +19 -0
  29. package/bin/domain/usecases/IssueRejectionEvaluator.js.map +1 -1
  30. package/bin/domain/usecases/QualityCheckAdvanceUseCase.js +1 -0
  31. package/bin/domain/usecases/QualityCheckAdvanceUseCase.js.map +1 -1
  32. package/bin/domain/usecases/ReopenedDoneIssueRevertUseCase.js +36 -0
  33. package/bin/domain/usecases/ReopenedDoneIssueRevertUseCase.js.map +1 -0
  34. package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js +2 -5
  35. package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js.map +1 -1
  36. package/package.json +1 -1
  37. package/src/adapter/entry-points/cli/index.ts +14 -10
  38. package/src/adapter/entry-points/console/consoleOperationApi.test.ts +199 -0
  39. package/src/adapter/entry-points/console/consoleOperationApi.ts +65 -0
  40. package/src/adapter/entry-points/console/consoleProjectResolver.test.ts +26 -0
  41. package/src/adapter/entry-points/console/consoleProjectResolver.ts +8 -1
  42. package/src/adapter/entry-points/console/dashboardEmitterComposerKey.test.ts +1 -0
  43. package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +40 -0
  44. package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +18 -0
  45. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.stories.tsx +4 -0
  46. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.test.tsx +114 -141
  47. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.tsx +84 -3
  48. package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.ts +23 -0
  49. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +73 -0
  50. package/src/adapter/entry-points/console/ui/src/index.css +35 -0
  51. package/src/adapter/entry-points/console/ui-dist/assets/index-wj6o8qvo.js +86 -0
  52. package/src/adapter/entry-points/console/ui-dist/assets/index-yZy1TUOo.css +2 -0
  53. package/src/adapter/entry-points/console/ui-dist/index.html +2 -2
  54. package/src/adapter/entry-points/console/webServer.ts +7 -0
  55. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +11 -0
  56. package/src/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.test.ts +1 -0
  57. package/src/adapter/entry-points/handlers/dashboardRowWriter.test.ts +1 -0
  58. package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.test.ts +1 -0
  59. package/src/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.test.ts +1 -0
  60. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +1 -0
  61. package/src/adapter/entry-points/handlers/ownerCallFileCleaner.test.ts +1 -0
  62. package/src/adapter/entry-points/handlers/situationFileWriter.test.ts +1 -0
  63. package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.test.ts +1 -0
  64. package/src/adapter/entry-points/handlers/tokenStatusWriter.test.ts +1 -0
  65. package/src/adapter/entry-points/handlers/tokenStatusWriter.ts +1 -0
  66. package/src/adapter/repositories/GitHubIssueCommentRepository.test.ts +1 -0
  67. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +90 -0
  68. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +48 -2
  69. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +107 -0
  70. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.ts +20 -0
  71. package/src/adapter/repositories/issue/RestIssueRepository.test.ts +1 -0
  72. package/src/domain/entities/Issue.ts +1 -0
  73. package/src/domain/entities/Project.ts +11 -2
  74. package/src/domain/usecases/AgentDesignationLabelAdoptUseCase.test.ts +1 -0
  75. package/src/domain/usecases/CheckIssueReviewReadinessUseCase.test.ts +1 -0
  76. package/src/domain/usecases/GetStoryObjectMapUseCase.test.ts +1 -0
  77. package/src/domain/usecases/HandleScheduledEventUseCase.test.ts +144 -0
  78. package/src/domain/usecases/HandleScheduledEventUseCase.ts +16 -0
  79. package/src/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.test.ts +1 -0
  80. package/src/domain/usecases/IssueNoStatusUpdateUseCase.test.ts +165 -0
  81. package/src/domain/usecases/IssueNoStatusUpdateUseCase.ts +44 -0
  82. package/src/domain/usecases/IssueRejectionEvaluator.test.ts +120 -0
  83. package/src/domain/usecases/IssueRejectionEvaluator.ts +21 -0
  84. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +1 -0
  85. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +1 -0
  86. package/src/domain/usecases/QualityCheckAdvanceUseCase.test.ts +34 -0
  87. package/src/domain/usecases/QualityCheckAdvanceUseCase.ts +1 -0
  88. package/src/domain/usecases/ReopenedDoneIssueRevertUseCase.test.ts +188 -0
  89. package/src/domain/usecases/ReopenedDoneIssueRevertUseCase.ts +57 -0
  90. package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.test.ts +44 -0
  91. package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.ts +4 -12
  92. package/src/domain/usecases/RevertOrphanedPreparationUseCase.test.ts +1 -0
  93. package/src/domain/usecases/SetupTowerDefenceProjectUseCase.test.ts +1 -0
  94. package/src/domain/usecases/StaleTmuxSessionKillUseCase.test.ts +1 -0
  95. package/src/domain/usecases/StartPreparationUseCase.test.ts +1 -0
  96. package/src/domain/usecases/TriagerApprovalDispatchUseCase.test.ts +1 -0
  97. package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +5 -0
  98. package/src/domain/usecases/console/GenerateConsoleListsUseCase.test.ts +1 -0
  99. package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.test.ts +1 -0
  100. package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.test.ts +1 -0
  101. package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.test.ts +1 -0
  102. package/src/domain/usecases/issueReactivationTriggerIsPending.test.ts +1 -0
  103. package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
  104. package/types/adapter/entry-points/console/consoleOperationApi.d.ts +2 -0
  105. package/types/adapter/entry-points/console/consoleOperationApi.d.ts.map +1 -1
  106. package/types/adapter/entry-points/console/consoleProjectResolver.d.ts +1 -0
  107. package/types/adapter/entry-points/console/consoleProjectResolver.d.ts.map +1 -1
  108. package/types/adapter/entry-points/console/webServer.d.ts +2 -0
  109. package/types/adapter/entry-points/console/webServer.d.ts.map +1 -1
  110. package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
  111. package/types/adapter/entry-points/handlers/tokenStatusWriter.d.ts.map +1 -1
  112. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts +4 -1
  113. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
  114. package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts +1 -0
  115. package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts.map +1 -1
  116. package/types/domain/entities/Issue.d.ts +1 -0
  117. package/types/domain/entities/Issue.d.ts.map +1 -1
  118. package/types/domain/entities/Project.d.ts +2 -1
  119. package/types/domain/entities/Project.d.ts.map +1 -1
  120. package/types/domain/usecases/HandleScheduledEventUseCase.d.ts +6 -1
  121. package/types/domain/usecases/HandleScheduledEventUseCase.d.ts.map +1 -1
  122. package/types/domain/usecases/IssueNoStatusUpdateUseCase.d.ts +12 -0
  123. package/types/domain/usecases/IssueNoStatusUpdateUseCase.d.ts.map +1 -0
  124. package/types/domain/usecases/IssueRejectionEvaluator.d.ts +1 -0
  125. package/types/domain/usecases/IssueRejectionEvaluator.d.ts.map +1 -1
  126. package/types/domain/usecases/QualityCheckAdvanceUseCase.d.ts.map +1 -1
  127. package/types/domain/usecases/ReopenedDoneIssueRevertUseCase.d.ts +12 -0
  128. package/types/domain/usecases/ReopenedDoneIssueRevertUseCase.d.ts.map +1 -0
  129. package/types/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.d.ts.map +1 -1
  130. package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +3 -0
  131. package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
  132. package/bin/adapter/entry-points/console/ui-dist/assets/index-8Ni5iu-C.css +0 -2
  133. package/bin/adapter/entry-points/console/ui-dist/assets/index-DQT4HL0G.js +0 -86
  134. package/src/adapter/entry-points/console/ui-dist/assets/index-8Ni5iu-C.css +0 -2
  135. package/src/adapter/entry-points/console/ui-dist/assets/index-DQT4HL0G.js +0 -86
@@ -0,0 +1,2 @@
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-border-style:solid;--tw-font-weight:initial;--tw-outline-style:solid;--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}}}@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;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--font-weight-medium:500;--font-weight-semibold:600;--radius-md:.375rem;--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-input:#e5e5e5;--color-ring:#0a0a0a;--color-background:#fff;--color-foreground:#0a0a0a;--color-primary:#171717;--color-primary-foreground:#fafafa;--color-secondary:#f5f5f5;--color-secondary-foreground:#171717;--color-accent:#f5f5f5;--color-accent-foreground:#171717}}@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;-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{.visible{visibility:visible}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.block{display:block}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.grow{flex-grow:1}.items-center{align-items:center}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-input{border-color:var(--color-input)}.border-transparent{border-color:#0000}.bg-background{background-color:var(--color-background)}.bg-primary{background-color:var(--color-primary)}.bg-secondary{background-color:var(--color-secondary)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-2{padding-block:calc(var(--spacing) * 2)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.whitespace-nowrap{white-space:nowrap}.text-foreground{color:var(--color-foreground)}.text-primary-foreground{color:var(--color-primary-foreground)}.text-secondary-foreground{color:var(--color-secondary-foreground)}.lowercase{text-transform:lowercase}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.hover\:bg-accent:hover{background-color:var(--color-accent)}.hover\:bg-primary\/90:hover{background-color:#171717e6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--color-primary) 90%, transparent)}}.hover\:bg-secondary\/80:hover{background-color:#f5f5f5cc}@supports (color:color-mix(in lab, red, red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab, var(--color-secondary) 80%, transparent)}}.hover\:text-accent-foreground:hover{color:var(--color-accent-foreground)}}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + 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)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:var(--color-ring)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}}html{font-size:clamp(15px,2.1vw,26px)}body{color:#e6edf3;background-color:#0d1117;margin:0;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif}.console-app{flex-direction:column;width:100%;min-height:100dvh;display:flex}.console-tabbar{-webkit-overflow-scrolling:touch;background:#161b22;border-bottom:2px solid #30363d;flex-wrap:nowrap;align-items:stretch;gap:0;min-height:42px;padding:0 8px;display:flex;overflow-x:auto}.console-tab{color:#8b949e;white-space:nowrap;cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;align-items:center;gap:6px;margin-bottom:-2px;padding:10px 14px;font-size:.8125rem;font-weight:500;line-height:1.2;text-decoration:none;display:inline-flex}.console-tab:hover{color:#e6edf3}.console-tab[data-active=true]{color:#e6edf3;border-bottom-color:#2f81f7;font-weight:700}.console-tab-badge{text-align:center;color:#e6edf3;background:#484f58;border-radius:20px;min-width:20px;padding:1px 7px;font-size:.6875rem;font-weight:700;line-height:1.5}.console-tab-badge[data-zero=true]{color:#8b949e;background:#30363d}.console-tab-pjname{color:#8b949e;align-self:center;margin-left:auto;padding:0 8px;font-size:.7188rem}.console-tab-geninfo{color:#8b949e;align-self:center;padding:0 4px;font-size:.6875rem}.console-tab-settings{align-self:center;padding:0 4px}.console-timer-settings-button{color:#8b949e;cursor:pointer;background:0 0;border:none;border-radius:4px;padding:4px 8px;font-size:1rem;line-height:1}.console-timer-settings-button:hover{color:#e6edf3;background:#21262d}.console-timer-settings-dialog{z-index:100;justify-content:flex-end;align-items:flex-start;padding:48px 16px 0;display:flex;position:fixed;inset:0}.console-timer-settings-backdrop{cursor:default;z-index:-1;background:#01040999;border:none;position:fixed;inset:0}.console-timer-settings-dialog-inner{background:#161b22;border:1px solid #30363d;border-radius:8px;min-width:280px;max-width:400px;max-height:80vh;padding:20px;overflow-y:auto}.console-timer-settings-title{color:#e6edf3;margin:0 0 16px;font-size:.9375rem;font-weight:600}.console-timer-settings-row{justify-content:space-between;align-items:center;margin-bottom:16px;display:flex}.console-timer-settings-label{color:#c9d1d9;font-size:.875rem}.console-timer-settings-loading{color:#8b949e;padding:8px 0;font-size:.875rem}.console-timer-settings-project-list{margin:0;padding:0;list-style:none}.console-timer-settings-project-row{border-bottom:1px solid #21262d;align-items:center;gap:8px;padding:6px 0;display:flex}.console-timer-settings-project-row:last-child{border-bottom:none}.console-timer-settings-pjcode{color:#c9d1d9;flex:1;font-family:ui-monospace,SFMono-Regular,monospace;font-size:.8125rem}.console-timer-settings-minutes-input{color:#e6edf3;text-align:right;background:#0d1117;border:1px solid #30363d;border-radius:4px;width:56px;padding:3px 6px;font-size:.8125rem}.console-timer-settings-minutes-label{color:#8b949e;width:24px;font-size:.75rem}.console-timer-settings-actions{justify-content:flex-end;margin-top:16px;display:flex}.console-timer-settings-save{color:#fff;cursor:pointer;background:#238636;border:1px solid #f0f6fc1a;border-radius:6px;padding:6px 14px;font-size:.8125rem;font-weight:500}.console-timer-settings-save:hover{background:#2ea043}.console-list{margin:0;padding:12px 18px 18px;list-style:none}.console-list-group{list-style:none}.console-item-row .console-item-icon{flex:none;margin-top:3px}.console-group-header{background:#0b0f14;border-bottom:1px solid #21262d;justify-content:space-between;align-items:center;padding:6px 12px;display:flex}.console-storytag{align-items:center;gap:8px;font-size:.8125rem;font-weight:700;display:inline-flex}.console-story-dot{border-radius:999px;width:10px;height:10px;display:inline-block}.console-group-count{color:#8b949e;font-size:.75rem}.console-story-color-palette{flex-wrap:wrap;gap:4px;padding:6px 0;display:flex}.console-story-color-swatch{cursor:pointer;color:#e6edf3;border:2px solid #0000;border-radius:6px;justify-content:center;align-items:center;width:28px;height:28px;padding:0;font-size:.625rem;display:inline-flex}.console-story-color-swatch:hover{border-color:#e6edf3}.console-story-color-swatch:disabled{opacity:.4;cursor:not-allowed}.console-story-color-swatch-label{font-size:.5rem;line-height:1}.console-item-row{color:#e6edf3;text-align:left;cursor:pointer;background:#161b22;border:1px solid #30363d;border-radius:8px;align-items:flex-start;gap:14px;width:100%;margin-bottom:10px;padding:12px 16px;display:flex}.console-item-row:hover{background:#1a2029;border-color:#484f58}.console-item-row[data-active=true]{background:#1a2029;border-color:#4493f8}.console-item-meta{flex:1;min-width:0}.console-item-title{font-size:.9063rem;font-weight:600;display:block}.console-item-sub{color:#8b949e;margin-top:3px;font-size:.7813rem;display:block}.console-item-pill{color:#8b949e;border:1px solid #30363d;border-radius:20px;margin-right:6px;padding:1px 8px;font-size:.6875rem;display:inline-block}.console-item-createdat{color:#8b949e;cursor:help}.console-item-fields{color:#adbac7;flex-wrap:wrap;gap:4px 10px;margin-top:4px;font-size:.7188rem;display:flex}.console-item-field{word-break:break-all;align-items:baseline;gap:4px;min-width:0;display:inline-flex}.console-item-field-label{color:#6e7681;text-transform:uppercase;letter-spacing:.03em;font-size:.625rem}.console-list-message{color:#8b949e;padding:16px;font-size:.875rem}.console-list-empty{text-align:center;color:#8b949e;padding:40px;font-size:.875rem}.console-list-error,.console-detail-fetch-error{color:#f85149}.console-detail-fetch-error-line{margin:0}.console-comment-notloaded,.console-files-notloaded,.console-commits-notloaded,.console-detail-body-notloaded{color:#8b949e}.console-detail{flex-direction:column;flex:1 0 auto;gap:12px;padding:16px 16px 0;display:flex}.console-detail-topline{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.console-detail-title{align-items:center;gap:8px;margin:0;font-size:1.25rem;display:flex}.console-detail-title-text{flex:1}.console-detail-number{color:#8b949e;font-weight:400}.console-detail-closed-label{color:#a371f7;font-size:.8125rem}.console-detail-subbar{align-items:center;gap:12px;font-size:.8125rem;display:flex}.console-detail-link{color:#4493f8}.console-detail-repo{color:#8b949e;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.console-copy-url-button{color:#c9d1d9;border:1px solid #30363d;border-radius:6px;height:24px;padding:0 8px;font-size:.75rem}.console-copy-url-button:hover{color:#e6edf3;background-color:#21262d}.console-detail-pill,.console-label-chip,.console-detail-status-chip{border:1px solid #30363d;border-radius:999px;padding:2px 8px;font-size:.75rem;display:inline-block}.console-detail-agent-chip{color:#8b949e;border:1px solid #30363d;border-radius:999px;padding:2px 8px;font-size:.75rem;font-weight:400;display:inline-block}.console-detail-pr-status{flex-wrap:wrap;align-items:center;gap:6px;display:inline-flex}.console-related-pr-group{align-items:center;gap:6px;display:inline-flex}.console-related-pr-label{color:#8b949e;font-size:.75rem}.console-detail-ci-missing{opacity:.85;font-weight:400}.console-detail-mergeable-chip{font-weight:600}.console-detail-mergeable-chip-unknown{opacity:.7;font-weight:400}.console-detail-labels{flex-wrap:wrap;gap:6px;display:flex}.console-detail-createdat{color:#8b949e;font-size:.75rem}.console-panel{border:1px solid #30363d;border-radius:8px;overflow:hidden}.console-panel-header{background:#161b22;justify-content:space-between;align-items:center;display:flex}.console-panel-toggle{color:#e6edf3;text-align:left;cursor:pointer;background:0 0;border:none;flex:1;align-items:center;gap:8px;padding:6px 12px;font-size:.875rem;font-weight:600;display:flex}.console-panel-toggle:hover{background:#1a2029}.console-panel-action{padding:6px 12px}.console-panel-body{padding:12px}.console-markdown{word-break:break-word;font-size:.875rem;line-height:1.5}.console-markdown ul{margin:.5em 0;padding-left:1.5em;list-style:outside}.console-markdown ol{margin:.5em 0;padding-left:1.5em;list-style:decimal}.console-markdown li{margin:.25em 0}.console-markdown table{border-collapse:collapse;margin:.5em 0}.console-markdown th,.console-markdown td{border:1px solid #30363d;padding:4px 8px}.console-markdown th{background:#161b22}.console-markdown h1,.console-markdown h2,.console-markdown h3,.console-markdown h4,.console-markdown h5,.console-markdown h6{margin:1em 0 .5em;font-weight:700;line-height:1.25}.console-markdown h1{font-size:1.6em}.console-markdown h2{font-size:1.4em}.console-markdown h3{font-size:1.2em}.console-markdown h4{font-size:1.05em}.console-markdown h5{font-size:.95em}.console-markdown h6{color:#8b949e;font-size:.9em}.console-markdown pre{background:#161b22;border:1px solid #30363d;border-radius:6px;margin:.5em 0;padding:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8125rem;line-height:1.45;overflow-x:auto}.console-markdown code{background:#6e768166;border-radius:4px;padding:.15em .4em;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em}.console-markdown pre code{font-size:inherit;background:0 0;border-radius:0;padding:0}.console-markdown-code-block{flex-direction:column;margin:.5em 0;display:flex}.console-markdown-code-block pre{margin:0}.console-markdown-code-copy-host{justify-content:flex-end;margin-bottom:4px;display:flex}.console-copy-code-button{color:#c9d1d9;background-color:#161b22;border:1px solid #30363d;border-radius:6px;height:24px;padding:0 8px;font-size:.75rem}.console-copy-code-button:hover{color:#e6edf3;background-color:#21262d}.console-markdown blockquote{color:#8b949e;border-left:3px solid #30363d;margin:.5em 0;padding:0 1em}.console-markdown a{color:#4493f8;text-decoration:none}.console-markdown a:hover{text-decoration:underline}.console-markdown-reference-host{display:inline}.console-markdown-reference{vertical-align:baseline;align-items:center;gap:.25em;display:inline-flex}.console-markdown-reference .console-item-icon{flex-shrink:0}.console-markdown-reference-title{text-decoration:none}.console-markdown-reference:hover .console-markdown-reference-title{text-decoration:underline}.console-markdown hr{border:none;border-top:1px solid #30363d;margin:1em 0}.console-mermaid-error{color:#f85149;font-size:.8125rem}.console-mermaid-enlarge{color:#e6edf3;background:#21262d;border:1px solid #30363d;border-radius:6px;width:100%;margin-top:6px;padding:10px 12px;font-size:.8125rem;display:block}.console-mermaid-modal{z-index:1000;background:#0d1117;flex-direction:column;display:flex;position:fixed;inset:0}.console-mermaid-modal-bar{border-bottom:1px solid #30363d;align-items:center;gap:8px;padding:8px 12px;display:flex}.console-mermaid-modal-button,.console-mermaid-modal-close{color:#e6edf3;background:#21262d;border:1px solid #30363d;border-radius:6px;min-width:44px;min-height:44px;font-size:1.125rem}.console-mermaid-modal-button:disabled{opacity:.4}.console-mermaid-modal-close{margin-left:auto}.console-mermaid-modal-scale{text-align:center;color:#8b949e;min-width:4.5rem;font-size:.875rem}.console-mermaid-modal-viewport{-webkit-overflow-scrolling:touch;flex:1;overflow:auto}.console-mermaid-modal-canvas{min-width:100%}.console-comment{background:#161b22;border:1px solid #30363d;border-radius:8px;margin-bottom:10px;padding:12px 16px}.console-comment-header{color:#8b949e;gap:8px;font-size:.75rem;display:flex}.console-comment-show-all{color:#e6edf3;cursor:pointer;background:#21262d;border:1px solid #30363d;border-radius:6px;margin-bottom:10px;padding:6px 12px;font-size:.75rem;font-weight:600}.console-comment-show-all:hover{background-color:#262c34;border-color:#484f58}.console-comment-author{color:#e6edf3;font-weight:600}.console-files,.console-commits{margin:0;padding:0;list-style:none}.console-commit{align-items:center;gap:8px;padding:4px 0;font-size:.8125rem;display:flex}.console-file{flex-direction:column;font-size:.8125rem;display:flex}.console-file-tree,.console-file-tree-children{margin:0;padding:0;list-style:none}.console-file-tree-dir{flex-direction:column;display:flex}.console-file-tree-dir-name{color:#8b949e;align-items:center;gap:6px;padding:4px 0;font-weight:600;display:flex}.console-file-tree-dir-icon{flex:none}.console-file-badge{text-align:center;border:1px solid;border-radius:4px;width:18px;font-size:.6875rem}.console-file-path,.console-commit-message{flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.console-file-add,.console-pr-add{color:#3fb950}.console-file-del,.console-pr-del{color:#f85149}.console-commit-sha{color:#8b949e;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.console-detail-dock{z-index:100;background:#161b22;border-top:2px solid #30363d;margin-top:auto;position:sticky;bottom:0}.console-detail-dock-composer{max-height:50vh;padding:8px 16px 0;overflow-y:auto}.console-actionbar{padding:10px 16px;padding-bottom:calc(10px + env(safe-area-inset-bottom));background:#161b22}.console-operation-bar{flex-direction:column;gap:8px;width:100%;display:flex}.console-op-group{flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:8px;display:flex}.console-op-group-review{gap:10px}.console-op-group-stories{max-height:33vh;overflow-y:auto}.console-op-button{color:#e6edf3;white-space:nowrap;cursor:pointer;background:#21262d;border:1px solid #30363d;border-radius:6px;padding:9px 16px;font-size:.8125rem;font-weight:600}.console-op-button:hover{border-color:#484f58}.console-op-button:disabled{opacity:.4;cursor:not-allowed}.console-op-requirement{text-align:right;color:#8b949e;width:100%;margin:0;font-size:.75rem}.console-op-button-approve{color:#fff;background:#238636;border-color:#2ea043}.console-op-button-reject{color:#ffd166;background:#7d5000;border-color:#a06800}.console-op-button-wrong{color:#f85149;background:#3a1518;border-color:#f85149}.console-op-button-unneeded{color:#aab0b8;background:#2a2d31;border-color:#6e7681}.console-op-button-snooze{color:#79c0ff;background:#1c2b4a;border-color:#4493f8}.console-pr-section{border:1px solid #30363d;border-radius:8px;flex-direction:column;gap:10px;padding:12px;display:flex}.console-pr-statbar{color:#8b949e;gap:12px;font-size:.75rem;display:flex}.console-pr-repo{color:#8b949e;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.console-detail-screen{flex-direction:column;flex:1 0 auto;display:flex}.console-panel-open-link{color:#4493f8;font-size:.8125rem;font-weight:400}.console-composer{margin-top:4px}.console-composer-toggle{color:#8b949e;cursor:pointer;background:0 0;border:none;margin-left:auto;padding:2px 0;font-size:.7813rem;display:block}.console-composer-toggle:hover{color:#e6edf3}.console-composer-form{margin-top:8px}.console-composer-input{box-sizing:border-box;color:#e6edf3;width:100%;font:inherit;resize:vertical;background:#21262d;border:1px solid #30363d;border-radius:6px;padding:8px;font-size:.875rem}.console-composer-attach{align-items:center;gap:8px;padding:0 10px;display:flex}.console-composer-file-input{display:none}.console-composer-attach-button{color:#c9d1d9;cursor:pointer;background:#21262d;border:1px solid #30363d;border-radius:4px;padding:4px 8px;font-size:.75rem}.console-composer-attach-button:disabled{opacity:.6;cursor:default}.console-composer-row{justify-content:flex-end;align-items:center;gap:8px;margin-top:6px;display:flex}.console-composer-submit{color:#fff;cursor:pointer;background:#238636;border:1px solid #2ea043;border-radius:6px;padding:7px 16px;font-size:.8125rem;font-weight:600}.console-composer-submit:disabled{opacity:.6;cursor:default}.console-composer-status{color:#8b949e;font-size:.75rem}.console-composer-error{color:#f85149}.console-undo-toast{z-index:9998;white-space:nowrap;border-radius:10px;align-items:center;gap:12px;max-width:92vw;padding:12px 18px;font-size:.8438rem;display:flex;position:fixed;top:12px;left:50%;transform:translate(-50%);box-shadow:0 4px 20px #0009}.console-undo-toast-green{color:#3fb950;background:#0d3320;border:1px solid #2ea043}.console-undo-toast-amber{color:#ffd166;background:#3a2800;border:1px solid #a06800}.console-undo-toast-red{color:#f85149;background:#3a1518;border:1px solid #da3633}.console-undo-toast-gray{color:#aab0b8;background:#2a2d31;border:1px solid #6e7681}.console-undo-toast-blue{color:#79c0ff;background:#0d1f40;border:1px solid #4493f8}.console-undo-toast-error{color:#f85149;background:#3a1518;border:1px solid #f85149}.console-undo-toast-message{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.console-undo-toast-undo{cursor:pointer;color:inherit;background:#ffffff1f;border:none;border-radius:6px;flex:none;padding:4px 12px;font-size:.8125rem;font-weight:700}.console-undo-toast-undo:hover{background:#ffffff38}.console-undo-toast-countdown{opacity:.7;flex:none;font-size:.6875rem}.console-undo-toast-bar{opacity:.5;background:currentColor;border-radius:0 0 10px 10px;height:3px;transition:width .1s linear;position:absolute;bottom:0;left:0}.console-file-row{width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;align-items:center;gap:8px;padding:4px 0;font-size:.8125rem;display:flex}.console-file-row:hover{background:#1a2029}.console-file-caret{color:#8b949e;flex:none;width:1em}.console-file-diff{border-collapse:collapse;width:100%;margin:4px 0 8px;font:.8125rem/1.6 ui-monospace,SFMono-Regular,Menlo,monospace}.console-file-diff td{white-space:pre-wrap;word-break:break-word;vertical-align:top;padding:0 10px}.console-diff-ln{text-align:right;color:#6e7681;-webkit-user-select:none;user-select:none;white-space:nowrap;border-right:1px solid #30363d;width:1%;padding:0 8px}.console-file-diff td.console-diff-ln{white-space:nowrap;word-break:normal;overflow-wrap:normal}.console-diff-add td{background:#2ea04326}.console-diff-add .console-diff-code{color:#aff5b4}.console-diff-del td{background:#f8514926}.console-diff-del .console-diff-code{color:#ffdcd7}.console-diff-hunk td{color:#79c0ff;background:#161b22}.console-diff-ctx .console-diff-code{color:#c9d1d9}.console-file-diff-empty{color:#8b949e;padding:8px 14px;font-size:.75rem}.console-file-diff-image{padding:8px 14px}.console-file-diff-image-content{object-fit:contain;object-position:left top;border:1px solid #30363d;border-radius:4px;max-width:100%;max-height:420px}.console-diff-comment-cell{text-align:center;-webkit-user-select:none;user-select:none;width:1%;padding:0 2px}.console-diff-comment-button{color:#6e7681;cursor:pointer;opacity:.6;background:0 0;border:1px solid #30363d;border-radius:4px;width:18px;height:18px;padding:0;font-size:.875rem;line-height:16px}.console-diff-row:hover .console-diff-comment-button,.console-diff-comment-button:focus-visible,.console-diff-comment-button[aria-expanded=true]{opacity:1;color:#fff;background:#1f6feb;border-color:#2f81f7}.console-diff-composer-row td{background:#0d1117;padding:6px 10px}.console-diff-composer{background:#161b22;border:1px solid #30363d;border-radius:6px;flex-direction:column;gap:6px;padding:8px;display:flex}.console-diff-composer-anchor{color:#8b949e;font-size:.6875rem}.console-diff-composer-input{resize:vertical;color:#c9d1d9;width:100%;font:inherit;background:#0d1117;border:1px solid #30363d;border-radius:6px;padding:6px 8px}.console-diff-composer-controls{align-items:center;gap:8px;display:flex}.console-diff-composer-submit{color:#fff;cursor:pointer;background:#238636;border:1px solid #238636;border-radius:6px;padding:4px 12px}.console-diff-composer-submit:disabled{opacity:.6;cursor:default}.console-diff-composer-cancel{color:#c9d1d9;cursor:pointer;background:0 0;border:1px solid #30363d;border-radius:6px;padding:4px 12px}.console-diff-composer-status{color:#8b949e;font-size:.75rem}.console-diff-composer-error{color:#ff7b72}.console-diff-composer-posted{color:#3fb950;margin:0;font-size:.75rem}.console-pr-header{flex-wrap:wrap;align-items:center;gap:10px;padding-top:4px;display:flex}.console-pr-section-title{color:#e6edf3;font-size:.9375rem;font-weight:600}.console-pr-section-state{color:#8b949e;border:1px solid #6e7681;border-radius:999px;padding:1px 8px;font-size:.6875rem}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@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}
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>TDPM Console</title>
7
- <script type="module" crossorigin src="/assets/index-DQT4HL0G.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-8Ni5iu-C.css">
7
+ <script type="module" crossorigin src="/assets/index-wj6o8qvo.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-yZy1TUOo.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
@@ -4,6 +4,7 @@ import * as path from 'path';
4
4
  import { IssueAttachmentRepository } from '../../../domain/usecases/adapter-interfaces/IssueAttachmentRepository';
5
5
  import { IssueRepository } from '../../../domain/usecases/adapter-interfaces/IssueRepository';
6
6
  import { ProjectRepository } from '../../../domain/usecases/adapter-interfaces/ProjectRepository';
7
+ import { Project } from '../../../domain/entities/Project';
7
8
  import {
8
9
  CONSOLE_LIST_TAB_NAMES,
9
10
  buildConsoleDataResponse,
@@ -34,6 +35,7 @@ import {
34
35
  handleReorderStory,
35
36
  handleReview,
36
37
  handleReviewComment,
38
+ handleStoryColor,
37
39
  handleTriage,
38
40
  } from './consoleOperationApi';
39
41
  import { ImageFetcher, fetchProxiedImage } from './consoleImageProxy';
@@ -239,6 +241,8 @@ export type WebServerOptions = {
239
241
  invalidateProject?: ((pjcode: string) => void) | null;
240
242
  issueAttachmentRepository?: IssueAttachmentRepository | null;
241
243
  projectRepository?: Pick<ProjectRepository, 'updateStoryList'> | null;
244
+ updateProjectCacheEntry?:
245
+ ((pjcode: string, updatedProject: Project) => void) | null;
242
246
  issueTitleStateCache?: IssueTitleStateCache | null;
243
247
  pullRequestStatusCache?: PullRequestStatusCache | null;
244
248
  };
@@ -546,6 +550,8 @@ const dispatchOperation = (
546
550
  return handleReorderStory(context, body);
547
551
  case '/api/addstory':
548
552
  return handleStoryAdd(context, body);
553
+ case '/api/storycolor':
554
+ return handleStoryColor(context, body);
549
555
  default:
550
556
  return null;
551
557
  }
@@ -582,6 +588,7 @@ const handleOperationApi = async (
582
588
  : null,
583
589
  resolveProjectRepository: options.resolveProjectRepository ?? null,
584
590
  invalidateProject: options.invalidateProject ?? null,
591
+ updateProjectCacheEntry: options.updateProjectCacheEntry ?? null,
585
592
  };
586
593
  const dispatched = dispatchOperation(context, requestPath, body);
587
594
  if (dispatched === null) {
@@ -61,6 +61,7 @@ import { SetNoStoryIssueToStoryUseCase } from '../../../domain/usecases/SetNoSto
61
61
  import { CreateNewStoryByLabelUseCase } from '../../../domain/usecases/CreateNewStoryByLabelUseCase';
62
62
  import { AssignNoAssigneeIssueToManagerUseCase } from '../../../domain/usecases/AssignNoAssigneeIssueToManagerUseCase';
63
63
  import { UpdateIssueStatusByLabelUseCase } from '../../../domain/usecases/UpdateIssueStatusByLabelUseCase';
64
+ import { IssueNoStatusUpdateUseCase } from '../../../domain/usecases/IssueNoStatusUpdateUseCase';
64
65
  import { StartPreparationUseCase } from '../../../domain/usecases/StartPreparationUseCase';
65
66
  import { NodeLocalCommandRunner } from '../../repositories/NodeLocalCommandRunner';
66
67
  import { ProcTakeOwnershipSpawnRepository } from '../../repositories/ProcTakeOwnershipSpawnRepository';
@@ -76,6 +77,7 @@ import { ProjectRequiredFieldCreateUseCase } from '../../../domain/usecases/Proj
76
77
  import { SetupTowerDefenceProjectUseCase } from '../../../domain/usecases/SetupTowerDefenceProjectUseCase';
77
78
  import { DailySecurityScanUseCase } from '../../../domain/usecases/DailySecurityScanUseCase';
78
79
  import { QualityCheckAdvanceUseCase } from '../../../domain/usecases/QualityCheckAdvanceUseCase';
80
+ import { ReopenedDoneIssueRevertUseCase } from '../../../domain/usecases/ReopenedDoneIssueRevertUseCase';
79
81
  import { KyHttpRepository } from '../../repositories/KyHttpRepository';
80
82
  import { FileSystemKevReportWatermarkRepository } from '../../repositories/FileSystemKevReportWatermarkRepository';
81
83
  import {
@@ -468,6 +470,9 @@ export class HandleScheduledEventUseCaseHandler {
468
470
  const updateIssueStatusByLabelUseCase = new UpdateIssueStatusByLabelUseCase(
469
471
  issueRepository,
470
472
  );
473
+ const issueNoStatusUpdateUseCase = new IssueNoStatusUpdateUseCase(
474
+ issueRepository,
475
+ );
471
476
  const nodeLocalCommandRunner = new NodeLocalCommandRunner();
472
477
  const claudeTokenUsageRepository = new ProxyClaudeTokenUsageRepository(
473
478
  mergedInput.claudeCodeOauthTokenListJsonPath ?? null,
@@ -522,6 +527,10 @@ export class HandleScheduledEventUseCaseHandler {
522
527
  issueRepository,
523
528
  );
524
529
 
530
+ const reopenedDoneIssueRevertUseCase = new ReopenedDoneIssueRevertUseCase(
531
+ issueRepository,
532
+ );
533
+
525
534
  const handleScheduledEventUseCase = new HandleScheduledEventUseCase(
526
535
  projectRequiredFieldCreateUseCase,
527
536
  setupTowerDefenceProjectUseCase,
@@ -540,6 +549,7 @@ export class HandleScheduledEventUseCaseHandler {
540
549
  createNewStoryByLabel,
541
550
  assignNoAssigneeIssueToManagerUseCase,
542
551
  updateIssueStatusByLabelUseCase,
552
+ issueNoStatusUpdateUseCase,
543
553
  startPreparationUseCase,
544
554
  revertOrphanedPreparationUseCase,
545
555
  revertNotReadyReviewQueueIssueUseCase,
@@ -548,6 +558,7 @@ export class HandleScheduledEventUseCaseHandler {
548
558
  updateRateLimitCacheUseCase,
549
559
  dailySecurityScanUseCase,
550
560
  qualityCheckAdvanceUseCase,
561
+ reopenedDoneIssueRevertUseCase,
551
562
  systemDateRepository,
552
563
  googleSpreadsheetRepository,
553
564
  projectRepository,
@@ -47,6 +47,7 @@ const issue = (url: string, status: string): Issue => ({
47
47
  author: 'hiromi',
48
48
  closingIssueReferenceUrls: [],
49
49
  agent: null,
50
+ stateReason: null,
50
51
  });
51
52
 
52
53
  describe('InTmuxByHumanSessionTokenCountHandler', () => {
@@ -38,6 +38,7 @@ const makeIssue = (overrides: Partial<Issue>): Issue => {
38
38
  author: 'someone',
39
39
  closingIssueReferenceUrls: [],
40
40
  agent: null,
41
+ stateReason: null,
41
42
  ...overrides,
42
43
  };
43
44
  };
@@ -67,6 +67,7 @@ const makeIssue = (overrides: Partial<Issue>): Issue => ({
67
67
  author: 'someone',
68
68
  closingIssueReferenceUrls: [],
69
69
  agent: null,
70
+ stateReason: null,
70
71
  ...overrides,
71
72
  });
72
73
 
@@ -32,6 +32,7 @@ const makeIssue = (overrides: Partial<Issue> = {}): Issue => ({
32
32
  author: '',
33
33
  closingIssueReferenceUrls: [],
34
34
  agent: null,
35
+ stateReason: null,
35
36
  ...overrides,
36
37
  });
37
38
 
@@ -501,6 +501,7 @@ describe('notifySilentTmuxSessions', () => {
501
501
  author: 'HiromiShikata',
502
502
  closingIssueReferenceUrls: [],
503
503
  agent: null,
504
+ stateReason: null,
504
505
  });
505
506
 
506
507
  it('skips a URL-named session whose hub task is no longer in the active status', async () => {
@@ -32,6 +32,7 @@ const makeIssue = (overrides: Partial<Issue> = {}): Issue => ({
32
32
  author: '',
33
33
  closingIssueReferenceUrls: [],
34
34
  agent: null,
35
+ stateReason: null,
35
36
  ...overrides,
36
37
  });
37
38
 
@@ -41,6 +41,7 @@ const createIssue = (overrides: Partial<Issue> = {}): Issue => ({
41
41
  author: 'user',
42
42
  closingIssueReferenceUrls: [],
43
43
  agent: null,
44
+ stateReason: null,
44
45
  ...overrides,
45
46
  });
46
47
 
@@ -52,6 +52,7 @@ const makeIssue = (overrides: Partial<Issue> = {}): Issue => ({
52
52
  author: '',
53
53
  closingIssueReferenceUrls: [],
54
54
  agent: null,
55
+ stateReason: null,
55
56
  ...overrides,
56
57
  });
57
58
 
@@ -59,6 +59,7 @@ const makeIssue = (overrides: Partial<Issue>): Issue => ({
59
59
  author: 'someone',
60
60
  closingIssueReferenceUrls: [],
61
61
  agent: null,
62
+ stateReason: null,
62
63
  ...overrides,
63
64
  });
64
65
 
@@ -232,6 +232,7 @@ export const writeTokenStatus = (params: TokenStatusWriterParams): void => {
232
232
  author: '',
233
233
  closingIssueReferenceUrls: [],
234
234
  agent: null,
235
+ stateReason: null,
235
236
  }));
236
237
  const humResult = new InTmuxByHumanSessionTokenCountUseCase().run(
237
238
  candidates,
@@ -27,6 +27,7 @@ const buildIssue = (url: string): Issue => ({
27
27
  author: 'testuser',
28
28
  closingIssueReferenceUrls: [],
29
29
  agent: null,
30
+ stateReason: null,
30
31
  });
31
32
 
32
33
  const TEST_URL = 'https://github.com/HiromiShikata/test-repo/issues/123';
@@ -73,6 +73,7 @@ const buildProjectItem = (url: string, title: string): ProjectItem => ({
73
73
  author: '',
74
74
  closingIssueReferenceUrls: [],
75
75
  isRepoArchived: false,
76
+ stateReason: null,
76
77
  customFields: [],
77
78
  });
78
79
 
@@ -118,6 +119,7 @@ describe('ApiV3CheerioRestIssueRepository', () => {
118
119
  'https://github.com/HiromiShikata/test-repository/issues/7',
119
120
  ],
120
121
  isRepoArchived: false,
122
+ stateReason: 'REOPENED' as const,
121
123
  customFields: [
122
124
  { name: 'nextActionDate', value: '2000-01-01' },
123
125
  { name: 'nextActionHour', value: '1' },
@@ -156,6 +158,7 @@ describe('ApiV3CheerioRestIssueRepository', () => {
156
158
  ],
157
159
  agent: null,
158
160
  isRepoArchived: false,
161
+ stateReason: 'REOPENED',
159
162
  },
160
163
  },
161
164
  {
@@ -176,6 +179,7 @@ describe('ApiV3CheerioRestIssueRepository', () => {
176
179
  author: '',
177
180
  closingIssueReferenceUrls: [],
178
181
  isRepoArchived: false,
182
+ stateReason: null,
179
183
  customFields: [
180
184
  {
181
185
  name: 'DependedIssueUrls',
@@ -215,6 +219,7 @@ describe('ApiV3CheerioRestIssueRepository', () => {
215
219
  closingIssueReferenceUrls: [],
216
220
  agent: null,
217
221
  isRepoArchived: false,
222
+ stateReason: null,
218
223
  },
219
224
  },
220
225
  ];
@@ -1018,6 +1023,7 @@ describe('ApiV3CheerioRestIssueRepository', () => {
1018
1023
  author: '',
1019
1024
  closingIssueReferenceUrls: [],
1020
1025
  agent: null,
1026
+ stateReason: null,
1021
1027
  };
1022
1028
  const statusId = 'new-status-id';
1023
1029
 
@@ -4815,6 +4821,90 @@ describe('ApiV3CheerioRestIssueRepository', () => {
4815
4821
  });
4816
4822
  });
4817
4823
 
4824
+ describe('updateStoryOptionColor', () => {
4825
+ it('preserves all other option ids and colors and changes only the target option color', async () => {
4826
+ const storyProject: Project & {
4827
+ story: NonNullable<Project['story']>;
4828
+ } = {
4829
+ ...buildTestProject('PVT_test'),
4830
+ story: {
4831
+ name: 'Story',
4832
+ fieldId: 'storyField',
4833
+ databaseId: 1,
4834
+ stories: [
4835
+ { id: 'opt_a', name: 'Alpha', color: 'BLUE', description: '' },
4836
+ { id: 'opt_b', name: 'Beta', color: 'GREEN', description: '' },
4837
+ { id: 'opt_c', name: 'Gamma', color: 'YELLOW', description: '' },
4838
+ ],
4839
+ workflowManagementStory: { id: 'wms', name: 'workflow' },
4840
+ },
4841
+ };
4842
+
4843
+ let capturedBody: unknown;
4844
+ jest
4845
+ .spyOn(global, 'fetch')
4846
+ .mockImplementationOnce(async (input): Promise<Response> => {
4847
+ const req = input instanceof Request ? input : new Request(input);
4848
+ capturedBody = JSON.parse(await req.text());
4849
+ return new Response(
4850
+ JSON.stringify({
4851
+ data: {
4852
+ updateProjectV2Field: {
4853
+ projectV2Field: { options: [] },
4854
+ },
4855
+ },
4856
+ }),
4857
+ { status: 200, headers: { 'Content-Type': 'application/json' } },
4858
+ );
4859
+ });
4860
+
4861
+ const { repository } = createApiV3CheerioRestIssueRepository();
4862
+ await repository.updateStoryOptionColor(storyProject, 'opt_b', 'RED');
4863
+
4864
+ expect(capturedBody).toMatchObject({
4865
+ variables: {
4866
+ fieldId: 'storyField',
4867
+ options: [
4868
+ { id: 'opt_a', name: 'Alpha', color: 'BLUE', description: '' },
4869
+ { id: 'opt_b', name: 'Beta', color: 'RED', description: '' },
4870
+ { id: 'opt_c', name: 'Gamma', color: 'YELLOW', description: '' },
4871
+ ],
4872
+ },
4873
+ });
4874
+ });
4875
+
4876
+ it('throws when the GitHub GraphQL response contains errors', async () => {
4877
+ const storyProject: Project & {
4878
+ story: NonNullable<Project['story']>;
4879
+ } = {
4880
+ ...buildTestProject('PVT_test'),
4881
+ story: {
4882
+ name: 'Story',
4883
+ fieldId: 'storyField',
4884
+ databaseId: 1,
4885
+ stories: [
4886
+ { id: 'opt_a', name: 'Alpha', color: 'BLUE', description: '' },
4887
+ ],
4888
+ workflowManagementStory: { id: 'wms', name: 'workflow' },
4889
+ },
4890
+ };
4891
+
4892
+ jest.spyOn(global, 'fetch').mockResolvedValueOnce(
4893
+ new Response(
4894
+ JSON.stringify({
4895
+ errors: [{ message: 'insufficient permissions' }],
4896
+ }),
4897
+ { status: 200, headers: { 'Content-Type': 'application/json' } },
4898
+ ),
4899
+ );
4900
+
4901
+ const { repository } = createApiV3CheerioRestIssueRepository();
4902
+ await expect(
4903
+ repository.updateStoryOptionColor(storyProject, 'opt_a', 'RED'),
4904
+ ).rejects.toThrow('insufficient permissions');
4905
+ });
4906
+ });
4907
+
4818
4908
  const createApiV3CheerioRestIssueRepository = () => {
4819
4909
  const apiV3IssueRepository = mock<ApiV3IssueRepository>();
4820
4910
  const restIssueRepository = mock<RestIssueRepository>();
@@ -9,7 +9,7 @@ import {
9
9
  PullRequestReviewCommentSide,
10
10
  PullRequestReviewInlineLocation,
11
11
  } from '../../../domain/usecases/adapter-interfaces/IssueRepository';
12
- import { Project } from '../../../domain/entities/Project';
12
+ import { FieldOption, Project } from '../../../domain/entities/Project';
13
13
  import { Issue } from '../../../domain/entities/Issue';
14
14
  import { SearchedIssue } from '../../../domain/entities/SearchedIssue';
15
15
  import { StoryObjectMap } from '../../../domain/entities/StoryObjectMap';
@@ -28,7 +28,10 @@ import {
28
28
  ProjectIssuesCacheRepository,
29
29
  } from '../ProjectIssuesCacheRepository';
30
30
  import { BaseGitHubRepository } from '../BaseGitHubRepository';
31
- import { fetchGithubGraphql } from '../githubGraphqlClient';
31
+ import {
32
+ fetchGithubGraphql,
33
+ postGithubGraphqlJson,
34
+ } from '../githubGraphqlClient';
32
35
  import { normalizeFieldName } from '../utils';
33
36
  import { LocalStorageRepository } from '../LocalStorageRepository';
34
37
  import { Member } from '../../../domain/entities/Member';
@@ -687,6 +690,7 @@ export class ApiV3CheerioRestIssueRepository
687
690
  closingIssueReferenceUrls: item.closingIssueReferenceUrls,
688
691
  agent,
689
692
  isRepoArchived: item.isRepoArchived,
693
+ stateReason: item.stateReason,
690
694
  };
691
695
  };
692
696
  private restoreIssuesFromCache = (rawIssues: unknown): Issue[] | null => {
@@ -723,12 +727,20 @@ export class ApiV3CheerioRestIssueRepository
723
727
  ? issue.closingIssueReferenceUrls
724
728
  : [];
725
729
 
730
+ const stateReason =
731
+ 'stateReason' in issue &&
732
+ (issue.stateReason === 'COMPLETED' ||
733
+ issue.stateReason === 'NOT_PLANNED' ||
734
+ issue.stateReason === 'REOPENED')
735
+ ? issue.stateReason
736
+ : null;
726
737
  return {
727
738
  ...issue,
728
739
  nextActionDate: nextActionDate,
729
740
  completionDate50PercentConfidence: completionDate50PercentConfidence,
730
741
  createdAt: createdAt,
731
742
  closingIssueReferenceUrls: closingIssueReferenceUrls,
743
+ stateReason: stateReason,
732
744
  };
733
745
  });
734
746
 
@@ -1071,6 +1083,40 @@ export class ApiV3CheerioRestIssueRepository
1071
1083
  { singleSelectOptionId: storyOptionId },
1072
1084
  );
1073
1085
  };
1086
+ updateStoryOptionColor = async (
1087
+ project: Project & { story: NonNullable<Project['story']> },
1088
+ storyOptionId: string,
1089
+ newColor: FieldOption['color'],
1090
+ ): Promise<void> => {
1091
+ const mutation = `mutation UpdateStoryOptionColor($fieldId: ID!, $options: [ProjectV2SingleSelectFieldOptionInput!]!) {
1092
+ updateProjectV2Field(input: {
1093
+ fieldId: $fieldId
1094
+ singleSelectOptions: $options
1095
+ }) {
1096
+ projectV2Field {
1097
+ ... on ProjectV2SingleSelectField {
1098
+ options { id name color description }
1099
+ }
1100
+ }
1101
+ }
1102
+ }`;
1103
+ const options = project.story.stories.map((opt) => ({
1104
+ id: opt.id,
1105
+ name: opt.name,
1106
+ color: opt.id === storyOptionId ? newColor : opt.color,
1107
+ description: opt.description,
1108
+ }));
1109
+ const response = await postGithubGraphqlJson<{
1110
+ errors?: { message: string }[];
1111
+ }>({
1112
+ ghToken: this.ghToken,
1113
+ query: mutation,
1114
+ variables: { fieldId: project.story.fieldId, options },
1115
+ });
1116
+ if (response.errors && response.errors.length > 0) {
1117
+ throw new Error(response.errors.map((e) => e.message).join('; '));
1118
+ }
1119
+ };
1074
1120
  clearProjectField = async (
1075
1121
  project: Project,
1076
1122
  fieldId: string,
@@ -781,6 +781,80 @@ describe('GraphqlProjectItemRepository', () => {
781
781
  );
782
782
  warnSpy.mockRestore();
783
783
  });
784
+
785
+ it('requests stateReason in the Issue content fragment of the GraphQL query', async () => {
786
+ const repository = new GraphqlProjectItemRepository(
787
+ new LocalStorageRepository(),
788
+ 'dummy-token',
789
+ );
790
+ mockPost.mockReturnValueOnce(makePageResponse(false, 'cursor-1', 1));
791
+
792
+ await repository.fetchProjectItems('test-project-id');
793
+
794
+ const sentQuery = extractRequestedQueryFromMockCall(
795
+ mockPost.mock.calls[0],
796
+ );
797
+ expect(sentQuery).toContain('stateReason');
798
+ });
799
+
800
+ it('maps stateReason REOPENED from Issue content node to ProjectItem.stateReason', async () => {
801
+ const repository = new GraphqlProjectItemRepository(
802
+ new LocalStorageRepository(),
803
+ 'dummy-token',
804
+ );
805
+ mockPost.mockReturnValueOnce(
806
+ mockJsonResponse({
807
+ data: {
808
+ node: {
809
+ items: {
810
+ totalCount: 1,
811
+ pageInfo: {
812
+ endCursor: 'cursor-1',
813
+ startCursor: 'cursor-start',
814
+ hasNextPage: false,
815
+ },
816
+ nodes: [
817
+ {
818
+ id: 'item-1',
819
+ fieldValues: { nodes: [] },
820
+ content: {
821
+ repository: { nameWithOwner: 'owner/repo' },
822
+ number: 1,
823
+ title: 'Test Issue',
824
+ state: 'OPEN',
825
+ stateReason: 'REOPENED',
826
+ url: 'https://github.com/owner/repo/issues/1',
827
+ body: 'body',
828
+ createdAt: '2024-01-01T00:00:00Z',
829
+ labels: { nodes: [] },
830
+ assignees: { nodes: [] },
831
+ },
832
+ },
833
+ ],
834
+ },
835
+ },
836
+ },
837
+ }),
838
+ );
839
+
840
+ const result = await repository.fetchProjectItems('test-project-id');
841
+
842
+ expect(result).toHaveLength(1);
843
+ expect(result[0].stateReason).toBe('REOPENED');
844
+ });
845
+
846
+ it('maps stateReason to null when the field is absent from the content node', async () => {
847
+ const repository = new GraphqlProjectItemRepository(
848
+ new LocalStorageRepository(),
849
+ 'dummy-token',
850
+ );
851
+ mockPost.mockReturnValueOnce(makePageResponse(false, 'cursor-1', 1));
852
+
853
+ const result = await repository.fetchProjectItems('test-project-id');
854
+
855
+ expect(result).toHaveLength(1);
856
+ expect(result[0].stateReason).toBeNull();
857
+ });
784
858
  });
785
859
 
786
860
  const extractRequestedVariablesFromMockCall = (
@@ -1665,5 +1739,38 @@ describe('GraphqlProjectItemRepository', () => {
1665
1739
 
1666
1740
  warnSpy.mockRestore();
1667
1741
  });
1742
+
1743
+ it('maps stateReason from the single-item Issue response', async () => {
1744
+ const localStorageRepository = new LocalStorageRepository();
1745
+ const repository = new GraphqlProjectItemRepository(
1746
+ localStorageRepository,
1747
+ 'dummy-token',
1748
+ );
1749
+
1750
+ mockPost.mockReturnValueOnce(
1751
+ mockJsonResponse({
1752
+ data: {
1753
+ repository: {
1754
+ issue: {
1755
+ ...makeContentNode(
1756
+ 'https://github.com/owner/repo/issues/42',
1757
+ 42,
1758
+ 'Reopened Issue',
1759
+ ),
1760
+ stateReason: 'REOPENED',
1761
+ },
1762
+ pullRequest: null,
1763
+ },
1764
+ },
1765
+ }),
1766
+ );
1767
+
1768
+ const result = await repository.fetchProjectItemByUrl(
1769
+ 'https://github.com/owner/repo/issues/42',
1770
+ );
1771
+
1772
+ expect(result).not.toBeNull();
1773
+ expect(result?.stateReason).toBe('REOPENED');
1774
+ });
1668
1775
  });
1669
1776
  });