github-issue-tower-defence-management 1.90.0 → 1.91.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 (155) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +14 -1
  3. package/bin/adapter/entry-points/cli/index.js +16 -12
  4. package/bin/adapter/entry-points/cli/index.js.map +1 -1
  5. package/bin/adapter/entry-points/cli/projectConfig.js +2 -0
  6. package/bin/adapter/entry-points/cli/projectConfig.js.map +1 -1
  7. package/bin/adapter/entry-points/console/consoleOperationApi.js +54 -27
  8. package/bin/adapter/entry-points/console/consoleOperationApi.js.map +1 -1
  9. package/bin/adapter/entry-points/console/consoleProjectResolver.js +38 -0
  10. package/bin/adapter/entry-points/console/consoleProjectResolver.js.map +1 -0
  11. package/bin/adapter/entry-points/console/consoleServer.js +3 -4
  12. package/bin/adapter/entry-points/console/consoleServer.js.map +1 -1
  13. package/bin/adapter/entry-points/console/ui-dist/assets/index-BU6p3cGU.css +1 -0
  14. package/bin/adapter/entry-points/console/ui-dist/assets/index-BvuSQN9s.js +100 -0
  15. package/bin/adapter/entry-points/console/ui-dist/index.html +2 -2
  16. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +16 -0
  17. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
  18. package/jest.config.js +57 -9
  19. package/package.json +17 -13
  20. package/src/adapter/entry-points/cli/index.test.ts +12 -2
  21. package/src/adapter/entry-points/cli/index.ts +30 -12
  22. package/src/adapter/entry-points/cli/projectConfig.ts +3 -0
  23. package/src/adapter/entry-points/console/consoleOperationApi.test.ts +129 -15
  24. package/src/adapter/entry-points/console/consoleOperationApi.ts +83 -28
  25. package/src/adapter/entry-points/console/consoleProjectResolver.test.ts +96 -0
  26. package/src/adapter/entry-points/console/consoleProjectResolver.ts +50 -0
  27. package/src/adapter/entry-points/console/consoleServer.test.ts +5 -4
  28. package/src/adapter/entry-points/console/consoleServer.ts +5 -7
  29. package/src/adapter/entry-points/console/ui/jest.setup.ts +1 -0
  30. package/src/adapter/entry-points/console/ui/jest.styleMock.js +1 -0
  31. package/src/adapter/entry-points/console/ui/src/features/console/colors.test.ts +34 -0
  32. package/src/adapter/entry-points/console/ui/src/features/console/colors.ts +73 -0
  33. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleChangedFileList.stories.tsx +28 -0
  34. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleChangedFileList.test.tsx +42 -0
  35. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleChangedFileList.tsx +55 -0
  36. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleCloseButtonGroup.stories.tsx +14 -0
  37. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleCloseButtonGroup.test.tsx +23 -0
  38. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleCloseButtonGroup.tsx +26 -0
  39. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleCommentList.stories.tsx +29 -0
  40. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleCommentList.test.tsx +55 -0
  41. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleCommentList.tsx +66 -0
  42. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleCommitList.stories.tsx +25 -0
  43. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleCommitList.test.tsx +53 -0
  44. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleCommitList.tsx +53 -0
  45. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleItemDetail.stories.tsx +79 -0
  46. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleItemDetail.test.tsx +81 -0
  47. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleItemDetail.tsx +226 -0
  48. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleItemIcon.stories.tsx +82 -0
  49. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleItemIcon.test.tsx +52 -0
  50. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleItemIcon.tsx +32 -0
  51. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleListItemRow.stories.tsx +25 -0
  52. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleListItemRow.test.tsx +43 -0
  53. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleListItemRow.tsx +34 -0
  54. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleListView.stories.tsx +22 -6
  55. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleListView.test.tsx +87 -0
  56. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleListView.tsx +36 -32
  57. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleMarkdownView.stories.tsx +27 -0
  58. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleMarkdownView.test.tsx +36 -0
  59. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleMarkdownView.tsx +50 -0
  60. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleMermaidDiagram.stories.tsx +22 -0
  61. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleMermaidDiagram.test.tsx +38 -0
  62. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleMermaidDiagram.tsx +65 -0
  63. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleNextActionDateGroup.stories.tsx +20 -0
  64. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleNextActionDateGroup.test.tsx +42 -0
  65. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleNextActionDateGroup.tsx +28 -0
  66. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleOperationBar.stories.tsx +55 -0
  67. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleOperationBar.test.tsx +85 -0
  68. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleOperationBar.tsx +55 -0
  69. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsolePanel.stories.tsx +26 -0
  70. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsolePanel.test.tsx +32 -0
  71. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsolePanel.tsx +36 -0
  72. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleProjectHeader.test.tsx +14 -0
  73. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsolePullRequestReviewGroup.stories.tsx +14 -0
  74. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsolePullRequestReviewGroup.test.tsx +33 -0
  75. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsolePullRequestReviewGroup.tsx +34 -0
  76. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsolePullRequestSection.stories.tsx +31 -0
  77. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsolePullRequestSection.test.tsx +40 -0
  78. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsolePullRequestSection.tsx +88 -0
  79. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleStatusButtonGroup.stories.tsx +17 -0
  80. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleStatusButtonGroup.test.tsx +49 -0
  81. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleStatusButtonGroup.tsx +63 -0
  82. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleStoryButtonGroup.stories.tsx +17 -0
  83. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleStoryButtonGroup.test.tsx +45 -0
  84. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleStoryButtonGroup.tsx +42 -0
  85. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleStoryGroupHeader.stories.tsx +27 -0
  86. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleStoryGroupHeader.test.tsx +24 -0
  87. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleStoryGroupHeader.tsx +28 -0
  88. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleTabBar.stories.tsx +41 -5
  89. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleTabBar.test.tsx +59 -0
  90. package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleTabBar.tsx +28 -19
  91. package/src/adapter/entry-points/console/ui/src/features/console/fileStatus.test.ts +35 -0
  92. package/src/adapter/entry-points/console/ui/src/features/console/fileStatus.ts +21 -0
  93. package/src/adapter/entry-points/console/ui/src/features/console/fixtures.ts +206 -9
  94. package/src/adapter/entry-points/console/ui/src/features/console/grouping.test.ts +91 -0
  95. package/src/adapter/entry-points/console/ui/src/features/console/grouping.ts +79 -0
  96. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.test.ts +22 -0
  97. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.ts +42 -0
  98. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.test.ts +126 -0
  99. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.ts +167 -0
  100. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.test.ts +198 -0
  101. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.ts +243 -0
  102. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOverlay.test.ts +40 -0
  103. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOverlay.ts +71 -0
  104. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleResource.test.ts +41 -0
  105. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleResource.ts +57 -0
  106. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.test.ts +63 -0
  107. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.ts +129 -0
  108. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleToken.test.ts +41 -0
  109. package/src/adapter/entry-points/console/ui/src/features/console/itemIcons.test.ts +97 -0
  110. package/src/adapter/entry-points/console/ui/src/features/console/itemIcons.ts +95 -0
  111. package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.test.ts +155 -0
  112. package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.ts +187 -0
  113. package/src/adapter/entry-points/console/ui/src/features/console/lib/markdown.test.ts +76 -0
  114. package/src/adapter/entry-points/console/ui/src/features/console/lib/markdown.ts +73 -0
  115. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidLoader.test.ts +27 -0
  116. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidLoader.ts +71 -0
  117. package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.test.ts +56 -0
  118. package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.ts +51 -0
  119. package/src/adapter/entry-points/console/ui/src/features/console/operations.test.ts +37 -0
  120. package/src/adapter/entry-points/console/ui/src/features/console/operations.ts +35 -0
  121. package/src/adapter/entry-points/console/ui/src/features/console/overlay.test.ts +124 -0
  122. package/src/adapter/entry-points/console/ui/src/features/console/overlay.ts +101 -0
  123. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +79 -0
  124. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +109 -0
  125. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.test.tsx +74 -0
  126. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +133 -7
  127. package/src/adapter/entry-points/console/ui/src/features/console/relativeTime.test.ts +52 -0
  128. package/src/adapter/entry-points/console/ui/src/features/console/relativeTime.ts +51 -0
  129. package/src/adapter/entry-points/console/ui/src/features/console/types.ts +73 -1
  130. package/src/adapter/entry-points/console/ui/src/index.css +352 -2
  131. package/src/adapter/entry-points/console/ui/tsconfig.json +1 -0
  132. package/src/adapter/entry-points/console/ui/vite.config.ts +5 -0
  133. package/src/adapter/entry-points/console/ui-dist/assets/index-BU6p3cGU.css +1 -0
  134. package/src/adapter/entry-points/console/ui-dist/assets/index-BvuSQN9s.js +100 -0
  135. package/src/adapter/entry-points/console/ui-dist/index.html +2 -2
  136. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +25 -0
  137. package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +4 -0
  138. package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
  139. package/types/adapter/entry-points/cli/projectConfig.d.ts +1 -0
  140. package/types/adapter/entry-points/cli/projectConfig.d.ts.map +1 -1
  141. package/types/adapter/entry-points/console/consoleOperationApi.d.ts +6 -2
  142. package/types/adapter/entry-points/console/consoleOperationApi.d.ts.map +1 -1
  143. package/types/adapter/entry-points/console/consoleProjectResolver.d.ts +6 -0
  144. package/types/adapter/entry-points/console/consoleProjectResolver.d.ts.map +1 -0
  145. package/types/adapter/entry-points/console/consoleServer.d.ts +2 -3
  146. package/types/adapter/entry-points/console/consoleServer.d.ts.map +1 -1
  147. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts +1 -0
  148. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
  149. package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +1 -0
  150. package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
  151. package/bin/adapter/entry-points/console/ui-dist/assets/index-DDjYPXRT.js +0 -49
  152. package/bin/adapter/entry-points/console/ui-dist/assets/index-DHlBLm7d.css +0 -1
  153. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleList.ts +0 -78
  154. package/src/adapter/entry-points/console/ui-dist/assets/index-DDjYPXRT.js +0 -49
  155. package/src/adapter/entry-points/console/ui-dist/assets/index-DHlBLm7d.css +0 -1
@@ -1,9 +1,53 @@
1
- import type { ConsoleListItem } from './types';
1
+ import type {
2
+ ConsoleChangedFile,
3
+ ConsoleComment,
4
+ ConsoleCommit,
5
+ ConsoleFieldOption,
6
+ ConsoleIssueState,
7
+ ConsoleListItem,
8
+ ConsoleRelatedPullRequest,
9
+ ConsoleStoryColorSource,
10
+ } from './types';
11
+
12
+ export const consoleStatusOptionsFixture: ConsoleFieldOption[] = [
13
+ { id: 'f75ad846', name: 'Unread', color: 'ORANGE' },
14
+ { id: 'd1c19cce', name: 'Awaiting Workspace', color: 'BLUE' },
15
+ { id: 'f57f1ce9', name: 'Preparation', color: 'YELLOW' },
16
+ { id: 'fd313492', name: 'Failed Preparation', color: 'RED' },
17
+ { id: 'e9931e57', name: 'Todo by human', color: 'PINK' },
18
+ { id: 'c2d278b2', name: 'In Tmux by human', color: 'RED' },
19
+ { id: 'e9f6a726', name: 'In Tmux by agent', color: 'YELLOW' },
20
+ { id: 'bd8358eb', name: 'Icebox', color: 'GRAY' },
21
+ ];
22
+
23
+ export const consoleStoryOptionsFixture: ConsoleFieldOption[] = [
24
+ { id: '28415d6c', name: 'regular / workflow improvement', color: 'GRAY' },
25
+ { id: '1491051e', name: 'TDPM Console port', color: 'BLUE' },
26
+ { id: 'e35b3da2', name: 'regular / WORKFLOW BLOCKER', color: 'RED' },
27
+ {
28
+ id: 'd7cdcb61',
29
+ name: 'regular / regular payment invoice tax',
30
+ color: 'YELLOW',
31
+ },
32
+ {
33
+ id: 'f7cd5cbc',
34
+ name: 'Publish product documentation site',
35
+ color: 'GREEN',
36
+ },
37
+ { id: '564803ee', name: 'Move to Okinawa', color: 'PURPLE' },
38
+ ];
39
+
40
+ export const consoleStoryColorsFixture: ConsoleStoryColorSource = {
41
+ 'TDPM Console port': { color: 'BLUE' },
42
+ 'regular / workflow improvement': { color: 'GRAY' },
43
+ 'Publish product documentation site': { color: 'GREEN' },
44
+ 'Move to Okinawa': { color: 'PURPLE' },
45
+ };
2
46
 
3
47
  export const consoleListItemsFixture: ConsoleListItem[] = [
4
48
  {
5
- number: 844,
6
- title: 'Add serveConsole server skeleton under entry-points',
49
+ number: 851,
50
+ title: 'Add serveConsole subcommand under entry-points',
7
51
  url: 'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/pull/851',
8
52
  repo: 'HiromiShikata/npm-cli-github-issue-tower-defence-management',
9
53
  nameWithOwner:
@@ -16,20 +60,34 @@ export const consoleListItemsFixture: ConsoleListItem[] = [
16
60
  createdAt: '2026-06-17T02:14:33.000Z',
17
61
  },
18
62
  {
19
- number: 845,
63
+ number: 853,
20
64
  title:
21
- 'Scaffold React console UI under entry-points with build bundling and minimal tab view',
22
- url: 'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/845',
65
+ 'Add server-side console API handlers for read and operation endpoints',
66
+ url: 'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/pull/853',
23
67
  repo: 'HiromiShikata/npm-cli-github-issue-tower-defence-management',
24
68
  nameWithOwner:
25
69
  'HiromiShikata/npm-cli-github-issue-tower-defence-management',
26
70
  projectItemId: 'PVTI_lADOABCD1234zgABCD02',
27
71
  itemId: 'PVTI_lADOABCD1234zgABCD02',
28
- isPr: false,
72
+ isPr: true,
29
73
  story: 'TDPM Console port',
30
74
  labels: ['claude'],
31
75
  createdAt: '2026-06-17T05:48:09.000Z',
32
76
  },
77
+ {
78
+ number: 845,
79
+ title: 'Scaffold React console UI under entry-points with build bundling',
80
+ url: 'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/845',
81
+ repo: 'HiromiShikata/npm-cli-github-issue-tower-defence-management',
82
+ nameWithOwner:
83
+ 'HiromiShikata/npm-cli-github-issue-tower-defence-management',
84
+ projectItemId: 'PVTI_lADOABCD1234zgABCD03',
85
+ itemId: 'PVTI_lADOABCD1234zgABCD03',
86
+ isPr: false,
87
+ story: 'TDPM Console port',
88
+ labels: [],
89
+ createdAt: '2026-06-16T22:01:55.000Z',
90
+ },
33
91
  {
34
92
  number: 778,
35
93
  title: 'Add Sonnet to Opus weekly-limit fallback routing per token',
@@ -37,11 +95,150 @@ export const consoleListItemsFixture: ConsoleListItem[] = [
37
95
  repo: 'HiromiShikata/npm-cli-github-issue-tower-defence-management',
38
96
  nameWithOwner:
39
97
  'HiromiShikata/npm-cli-github-issue-tower-defence-management',
40
- projectItemId: 'PVTI_lADOABCD1234zgABCD03',
41
- itemId: 'PVTI_lADOABCD1234zgABCD03',
98
+ projectItemId: 'PVTI_lADOABCD1234zgABCD04',
99
+ itemId: 'PVTI_lADOABCD1234zgABCD04',
42
100
  isPr: false,
43
101
  story: 'regular / workflow improvement',
44
102
  labels: [],
45
103
  createdAt: '2026-06-12T23:01:55.000Z',
46
104
  },
105
+ {
106
+ number: 692,
107
+ title: 'Publish the generated documentation site to GitHub Pages',
108
+ url: 'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/692',
109
+ repo: 'HiromiShikata/npm-cli-github-issue-tower-defence-management',
110
+ nameWithOwner:
111
+ 'HiromiShikata/npm-cli-github-issue-tower-defence-management',
112
+ projectItemId: 'PVTI_lADOABCD1234zgABCD05',
113
+ itemId: 'PVTI_lADOABCD1234zgABCD05',
114
+ isPr: false,
115
+ story: 'Publish product documentation site',
116
+ labels: ['documentation'],
117
+ createdAt: '2026-06-10T11:42:00.000Z',
118
+ },
47
119
  ];
120
+
121
+ export const consoleMarkdownBodyFixture = `## Summary
122
+
123
+ This pull request adds the \`serveConsole\` subcommand that serves the static
124
+ console bundle and the token-protected \`/api\` endpoints.
125
+
126
+ - Adds an HTTP server bound to port 9981 by default
127
+ - Serves \`list.json\` per tab and the read APIs by \`url\`
128
+ - Records acted-on items into a cross-tab done store
129
+
130
+ ### Checklist
131
+
132
+ - [x] Unit tests for the data delivery routing
133
+ - [x] Token validation via \`?k=\` and the \`x-pv-token\` header
134
+ `;
135
+
136
+ export const consoleMermaidBodyFixture = `Here is the request flow:
137
+
138
+ \`\`\`mermaid
139
+ sequenceDiagram
140
+ participant Browser
141
+ participant ConsoleServer
142
+ participant IssueRepository
143
+ Browser->>ConsoleServer: GET /api/itembody?url=...
144
+ ConsoleServer->>IssueRepository: getIssueOrPullRequestBody(url)
145
+ IssueRepository-->>ConsoleServer: body markdown
146
+ ConsoleServer-->>Browser: { body }
147
+ \`\`\`
148
+
149
+ The diagram above is rendered lazily.
150
+ `;
151
+
152
+ export const consoleMermaidCodeFixture = `sequenceDiagram
153
+ participant Browser
154
+ participant ConsoleServer
155
+ participant IssueRepository
156
+ Browser->>ConsoleServer: GET /api/itembody?url=...
157
+ ConsoleServer->>IssueRepository: getIssueOrPullRequestBody(url)
158
+ IssueRepository-->>ConsoleServer: body markdown
159
+ ConsoleServer-->>Browser: { body }`;
160
+
161
+ export const consoleCommentsFixture: ConsoleComment[] = [
162
+ {
163
+ author: 'HiromiShikata',
164
+ body: 'Please split the token validation into its own tested function.',
165
+ createdAt: '2026-06-17T06:12:40.000Z',
166
+ },
167
+ {
168
+ author: 'github-actions',
169
+ body: 'All required checks have passed on this pull request.',
170
+ createdAt: '2026-06-17T07:48:11.000Z',
171
+ },
172
+ {
173
+ author: 'HiromiShikata',
174
+ body: 'Looks good now. Approving once the rebase is green.',
175
+ createdAt: '2026-06-17T09:03:27.000Z',
176
+ },
177
+ ];
178
+
179
+ export const consoleChangedFilesFixture: ConsoleChangedFile[] = [
180
+ {
181
+ path: 'src/adapter/entry-points/console/consoleServer.ts',
182
+ additions: 312,
183
+ deletions: 4,
184
+ status: 'added',
185
+ patch: null,
186
+ },
187
+ {
188
+ path: 'src/adapter/entry-points/console/consoleServer.test.ts',
189
+ additions: 268,
190
+ deletions: 0,
191
+ status: 'added',
192
+ patch: null,
193
+ },
194
+ {
195
+ path: 'package.json',
196
+ additions: 6,
197
+ deletions: 1,
198
+ status: 'modified',
199
+ patch: null,
200
+ },
201
+ ];
202
+
203
+ export const consoleCommitsFixture: ConsoleCommit[] = [
204
+ {
205
+ sha: '4f9c2a1b6d8e0f3a7c5b9d1e2f4a6c8b0d2e4f6a',
206
+ message: 'feat(console): add serveConsole subcommand and HTTP server',
207
+ author: 'HiromiShikata',
208
+ authoredAt: '2026-06-17T02:10:11.000Z',
209
+ },
210
+ {
211
+ sha: '8b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7d',
212
+ message: 'test(console): cover token validation and data routing',
213
+ author: 'HiromiShikata',
214
+ authoredAt: '2026-06-17T05:41:52.000Z',
215
+ },
216
+ ];
217
+
218
+ export const consoleRelatedPullRequestsFixture: ConsoleRelatedPullRequest[] = [
219
+ {
220
+ url: 'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/pull/849',
221
+ branchName: 'feature/849-react-console-scaffold',
222
+ createdAt: '2026-06-16T22:30:00.000Z',
223
+ isDraft: false,
224
+ isConflicted: false,
225
+ isPassedAllCiJob: true,
226
+ isCiStateSuccess: true,
227
+ isResolvedAllReviewComments: true,
228
+ isBranchOutOfDate: false,
229
+ missingRequiredCheckNames: [],
230
+ summary: {
231
+ title: 'Scaffold React console UI under entry-points with build bundling',
232
+ body: 'Adds the React + Vite + Tailwind scaffold and the committed `ui-dist` single artifact.',
233
+ additions: 1184,
234
+ deletions: 12,
235
+ changedFiles: 27,
236
+ },
237
+ },
238
+ ];
239
+
240
+ export const consoleIssueStateFixture: ConsoleIssueState = {
241
+ state: 'open',
242
+ merged: false,
243
+ isPullRequest: true,
244
+ };
@@ -0,0 +1,91 @@
1
+ import {
2
+ buildConsoleListRows,
3
+ CONSOLE_NO_STORY_LABEL,
4
+ resolveItemStory,
5
+ resolveStoryColorEnum,
6
+ } from './grouping';
7
+ import type { ConsoleListItem, ConsoleOverlay } from './types';
8
+
9
+ const item = (
10
+ overrides: Partial<ConsoleListItem> &
11
+ Pick<ConsoleListItem, 'number' | 'story'>,
12
+ ): ConsoleListItem => ({
13
+ title: `Item ${overrides.number}`,
14
+ url: `https://github.com/o/r/issues/${overrides.number}`,
15
+ repo: 'o/r',
16
+ nameWithOwner: 'o/r',
17
+ projectItemId: `PVTI_${overrides.number}`,
18
+ itemId: `PVTI_${overrides.number}`,
19
+ isPr: false,
20
+ labels: [],
21
+ createdAt: '2026-06-10T00:00:00.000Z',
22
+ ...overrides,
23
+ });
24
+
25
+ describe('resolveStoryColorEnum', () => {
26
+ it('reads a wrapped color object shape', () => {
27
+ expect(resolveStoryColorEnum({ s: { color: 'BLUE' } }, 's')).toBe('BLUE');
28
+ });
29
+
30
+ it('reads a bare enum shape', () => {
31
+ expect(resolveStoryColorEnum({ s: 'RED' }, 's')).toBe('RED');
32
+ });
33
+
34
+ it('returns null for an unknown story', () => {
35
+ expect(resolveStoryColorEnum({}, 's')).toBeNull();
36
+ });
37
+ });
38
+
39
+ describe('resolveItemStory', () => {
40
+ it('prefers the overlay story name', () => {
41
+ const overlay: ConsoleOverlay = {
42
+ PVTI_1: {
43
+ ts: 1,
44
+ mode: 'triage',
45
+ story: { name: 'Overlay', color: 'BLUE' },
46
+ },
47
+ };
48
+ expect(
49
+ resolveItemStory(item({ number: 1, story: 'Original' }), overlay),
50
+ ).toBe('Overlay');
51
+ });
52
+
53
+ it('uses the trimmed item story when no overlay story exists', () => {
54
+ expect(resolveItemStory(item({ number: 2, story: ' Real ' }), {})).toBe(
55
+ ' Real ',
56
+ );
57
+ });
58
+
59
+ it('falls back to the no-story label when empty', () => {
60
+ expect(resolveItemStory(item({ number: 3, story: ' ' }), {})).toBe(
61
+ CONSOLE_NO_STORY_LABEL,
62
+ );
63
+ });
64
+ });
65
+
66
+ describe('buildConsoleListRows', () => {
67
+ it('inserts a group header whenever the story changes and keeps array order', () => {
68
+ const items = [
69
+ item({ number: 1, story: 'Alpha' }),
70
+ item({ number: 2, story: 'Alpha' }),
71
+ item({ number: 3, story: 'Beta' }),
72
+ item({ number: 4, story: 'Alpha' }),
73
+ ];
74
+ const rows = buildConsoleListRows(items, {});
75
+ expect(rows.map((row) => row.kind)).toEqual([
76
+ 'group-header',
77
+ 'item',
78
+ 'item',
79
+ 'group-header',
80
+ 'item',
81
+ 'group-header',
82
+ 'item',
83
+ ]);
84
+ const firstHeader = rows[0];
85
+ expect(firstHeader.kind === 'group-header' && firstHeader.count).toBe(3);
86
+ });
87
+
88
+ it('returns no rows for an empty list', () => {
89
+ expect(buildConsoleListRows([], {})).toEqual([]);
90
+ });
91
+ });
@@ -0,0 +1,79 @@
1
+ import type {
2
+ ConsoleColor,
3
+ ConsoleListItem,
4
+ ConsoleOverlay,
5
+ ConsoleStoryColorSource,
6
+ } from './types';
7
+
8
+ export const CONSOLE_NO_STORY_LABEL = '(No story)';
9
+
10
+ export const resolveStoryColorEnum = (
11
+ storyColors: ConsoleStoryColorSource,
12
+ storyName: string,
13
+ ): ConsoleColor | null => {
14
+ const entry = storyColors[storyName];
15
+ if (entry === undefined) {
16
+ return null;
17
+ }
18
+ if (typeof entry === 'string') {
19
+ return entry;
20
+ }
21
+ return entry.color;
22
+ };
23
+
24
+ export const resolveItemStory = (
25
+ item: ConsoleListItem,
26
+ overlay: ConsoleOverlay,
27
+ ): string => {
28
+ const overlayKey =
29
+ item.projectItemId !== '' ? item.projectItemId : item.itemId;
30
+ const overlayEntry = overlay[overlayKey];
31
+ if (
32
+ overlayEntry?.story?.name !== undefined &&
33
+ overlayEntry.story.name !== ''
34
+ ) {
35
+ return overlayEntry.story.name;
36
+ }
37
+ const trimmed = item.story.trim();
38
+ return trimmed !== '' ? item.story : CONSOLE_NO_STORY_LABEL;
39
+ };
40
+
41
+ export type ConsoleListGroupRow = {
42
+ kind: 'group-header';
43
+ story: string;
44
+ count: number;
45
+ };
46
+
47
+ export type ConsoleListItemRow = {
48
+ kind: 'item';
49
+ item: ConsoleListItem;
50
+ };
51
+
52
+ export type ConsoleListRow = ConsoleListGroupRow | ConsoleListItemRow;
53
+
54
+ export const buildConsoleListRows = (
55
+ items: ConsoleListItem[],
56
+ overlay: ConsoleOverlay,
57
+ ): ConsoleListRow[] => {
58
+ const storyCounts = new Map<string, number>();
59
+ for (const item of items) {
60
+ const story = resolveItemStory(item, overlay);
61
+ storyCounts.set(story, (storyCounts.get(story) ?? 0) + 1);
62
+ }
63
+
64
+ const rows: ConsoleListRow[] = [];
65
+ let previousStory: string | null = null;
66
+ for (const item of items) {
67
+ const story = resolveItemStory(item, overlay);
68
+ if (story !== previousStory) {
69
+ rows.push({
70
+ kind: 'group-header',
71
+ story,
72
+ count: storyCounts.get(story) ?? 0,
73
+ });
74
+ previousStory = story;
75
+ }
76
+ rows.push({ kind: 'item', item });
77
+ }
78
+ return rows;
79
+ };
@@ -0,0 +1,22 @@
1
+ import { renderHook } from '@testing-library/react';
2
+ import { useConsoleCaches } from './useConsoleCaches';
3
+
4
+ describe('useConsoleCaches', () => {
5
+ beforeEach(() => {
6
+ localStorage.clear();
7
+ window.history.replaceState({}, '', '/?k=token');
8
+ });
9
+
10
+ it('exposes one cache per resource and a stable identity across renders', () => {
11
+ const { result, rerender } = renderHook(() => useConsoleCaches());
12
+ const first = result.current;
13
+ expect(first.body).toBeDefined();
14
+ expect(first.comments).toBeDefined();
15
+ expect(first.files).toBeDefined();
16
+ expect(first.commits).toBeDefined();
17
+ expect(first.relatedPrs).toBeDefined();
18
+ expect(first.state).toBeDefined();
19
+ rerender();
20
+ expect(result.current).toBe(first);
21
+ });
22
+ });
@@ -0,0 +1,42 @@
1
+ import { useMemo } from 'react';
2
+ import {
3
+ type ConsoleApiClient,
4
+ createConsoleApiClient,
5
+ } from '../lib/consoleApi';
6
+ import { ResourceCache } from '../lib/resourceCache';
7
+ import type {
8
+ ConsoleChangedFile,
9
+ ConsoleComment,
10
+ ConsoleCommit,
11
+ ConsoleIssueState,
12
+ ConsoleRelatedPullRequest,
13
+ } from '../types';
14
+ import { useConsoleToken } from './useConsoleToken';
15
+
16
+ export type ConsoleCaches = {
17
+ body: ResourceCache<string>;
18
+ comments: ResourceCache<ConsoleComment[]>;
19
+ files: ResourceCache<ConsoleChangedFile[]>;
20
+ commits: ResourceCache<ConsoleCommit[]>;
21
+ relatedPrs: ResourceCache<ConsoleRelatedPullRequest[]>;
22
+ state: ResourceCache<ConsoleIssueState>;
23
+ client: ConsoleApiClient;
24
+ };
25
+
26
+ export const useConsoleCaches = (): ConsoleCaches => {
27
+ const { appendToken } = useConsoleToken();
28
+ return useMemo(() => {
29
+ const client = createConsoleApiClient(appendToken);
30
+ return {
31
+ client,
32
+ body: new ResourceCache<string>(client.fetchItemBody),
33
+ comments: new ResourceCache<ConsoleComment[]>(client.fetchComments),
34
+ files: new ResourceCache<ConsoleChangedFile[]>(client.fetchPrFiles),
35
+ commits: new ResourceCache<ConsoleCommit[]>(client.fetchPrCommits),
36
+ relatedPrs: new ResourceCache<ConsoleRelatedPullRequest[]>(
37
+ client.fetchRelatedPrs,
38
+ ),
39
+ state: new ResourceCache<ConsoleIssueState>(client.fetchIssueState),
40
+ };
41
+ }, [appendToken]);
42
+ };
@@ -0,0 +1,126 @@
1
+ import { renderHook, waitFor } from '@testing-library/react';
2
+ import { ResourceCache } from '../lib/resourceCache';
3
+ import type {
4
+ ConsoleChangedFile,
5
+ ConsoleComment,
6
+ ConsoleCommit,
7
+ ConsoleIssueState,
8
+ ConsoleListItem,
9
+ ConsoleRelatedPullRequest,
10
+ } from '../types';
11
+ import type { ConsoleCaches } from './useConsoleCaches';
12
+ import { useConsoleItemDetailData } from './useConsoleItemDetailData';
13
+
14
+ const prItem: ConsoleListItem = {
15
+ number: 1,
16
+ title: 'PR',
17
+ url: 'https://github.com/o/r/pull/1',
18
+ repo: 'o/r',
19
+ nameWithOwner: 'o/r',
20
+ projectItemId: 'PVTI_1',
21
+ itemId: 'PVTI_1',
22
+ isPr: true,
23
+ story: 'Story',
24
+ labels: [],
25
+ createdAt: '2026-06-10T00:00:00.000Z',
26
+ };
27
+
28
+ const issueItem: ConsoleListItem = {
29
+ ...prItem,
30
+ isPr: false,
31
+ url: 'https://github.com/o/r/issues/2',
32
+ number: 2,
33
+ };
34
+
35
+ const buildCaches = (related: ConsoleRelatedPullRequest[]): ConsoleCaches => {
36
+ const client = {
37
+ fetchItemBody: async () => 'body',
38
+ fetchComments: async (): Promise<ConsoleComment[]> => [],
39
+ fetchPrFiles: async (): Promise<ConsoleChangedFile[]> => [
40
+ {
41
+ path: 'a.ts',
42
+ additions: 1,
43
+ deletions: 0,
44
+ status: 'added',
45
+ patch: null,
46
+ },
47
+ ],
48
+ fetchPrCommits: async (): Promise<ConsoleCommit[]> => [
49
+ {
50
+ sha: 'abc',
51
+ message: 'm',
52
+ author: 'a',
53
+ authoredAt: '2026-06-10T00:00:00.000Z',
54
+ },
55
+ ],
56
+ fetchRelatedPrs: async (): Promise<ConsoleRelatedPullRequest[]> => related,
57
+ fetchIssueState: async (): Promise<ConsoleIssueState> => ({
58
+ state: 'open',
59
+ merged: false,
60
+ isPullRequest: true,
61
+ }),
62
+ };
63
+ return {
64
+ client,
65
+ body: new ResourceCache(client.fetchItemBody),
66
+ comments: new ResourceCache(client.fetchComments),
67
+ files: new ResourceCache(client.fetchPrFiles),
68
+ commits: new ResourceCache(client.fetchPrCommits),
69
+ relatedPrs: new ResourceCache(client.fetchRelatedPrs),
70
+ state: new ResourceCache(client.fetchIssueState),
71
+ };
72
+ };
73
+
74
+ describe('useConsoleItemDetailData', () => {
75
+ it('loads body, files and commits for a PR item', async () => {
76
+ const caches = buildCaches([]);
77
+ const { result } = renderHook(() =>
78
+ useConsoleItemDetailData(caches, prItem),
79
+ );
80
+ await waitFor(() => {
81
+ expect(result.current.body).toBe('body');
82
+ expect(result.current.files.length).toBe(1);
83
+ expect(result.current.commits.length).toBe(1);
84
+ });
85
+ });
86
+
87
+ it('loads related pull request views for an issue item', async () => {
88
+ const related: ConsoleRelatedPullRequest[] = [
89
+ {
90
+ url: 'https://github.com/o/r/pull/9',
91
+ branchName: 'feat',
92
+ createdAt: '2026-06-10T00:00:00.000Z',
93
+ isDraft: false,
94
+ isConflicted: false,
95
+ isPassedAllCiJob: true,
96
+ isCiStateSuccess: true,
97
+ isResolvedAllReviewComments: true,
98
+ isBranchOutOfDate: false,
99
+ missingRequiredCheckNames: [],
100
+ summary: {
101
+ title: 'Linked PR',
102
+ body: 'body',
103
+ additions: 5,
104
+ deletions: 1,
105
+ changedFiles: 2,
106
+ },
107
+ },
108
+ ];
109
+ const caches = buildCaches(related);
110
+ const { result } = renderHook(() =>
111
+ useConsoleItemDetailData(caches, issueItem),
112
+ );
113
+ await waitFor(() => {
114
+ expect(result.current.relatedPullRequests.length).toBe(1);
115
+ expect(result.current.relatedPullRequests[0].filesAreLoading).toBe(false);
116
+ });
117
+ expect(result.current.relatedPullRequests[0].files.length).toBe(1);
118
+ });
119
+
120
+ it('returns defaults when no item is selected', () => {
121
+ const caches = buildCaches([]);
122
+ const { result } = renderHook(() => useConsoleItemDetailData(caches, null));
123
+ expect(result.current.body).toBe('');
124
+ expect(result.current.relatedPullRequests).toEqual([]);
125
+ });
126
+ });